git.delta.rocks / unique-network / refs/commits / 108965faa7da

difftreelog

Merge pull request #21 from usetech-llc/feature/NFTPAR-112

str-mv2020-11-27parents: #73335ba #f49194e.patch.diff
in: master
Remove unneeded CollectionAdminsType

1 file changed

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, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct CollectionAdminsType<AccountId> {119    pub admin: AccountId,120    pub collection_id: u64,121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct NftItemType<AccountId> {126    pub collection: u64,127    pub owner: AccountId,128    pub const_data: Vec<u8>,129    pub variable_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 const_data: Vec<u8>,146    pub variable_data: Vec<u8>,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct ApprovePermissions<AccountId> {152    pub approved: AccountId,153    pub amount: u64,154}155156#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]157#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]158pub struct VestingItem<AccountId, Moment> {159    pub sender: AccountId,160    pub recipient: AccountId,161    pub collection_id: u64,162    pub item_id: u64,163    pub amount: u64,164    pub vesting_date: Moment,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct BasketItem<AccountId, BlockNumber> {170    pub address: AccountId,171    pub start_block: BlockNumber,172}173174#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]175#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]176pub struct ChainLimits {177    pub collection_numbers_limit: u64,178    pub account_token_ownership_limit: u64,179    pub collections_admins_limit: u64,180    pub custom_data_limit: u32,181182    // Timeouts for item types in passed blocks183    pub nft_sponsor_transfer_timeout: u32,184    pub fungible_sponsor_transfer_timeout: u32,185    pub refungible_sponsor_transfer_timeout: u32,186}187188pub trait WeightInfo {189	fn create_collection() -> Weight;190	fn destroy_collection() -> Weight;191	fn add_to_white_list() -> Weight;192	fn remove_from_white_list() -> Weight;193    fn set_public_access_mode() -> Weight;194    fn set_mint_permission() -> Weight;195    fn change_collection_owner() -> Weight;196    fn add_collection_admin() -> Weight;197    fn remove_collection_admin() -> Weight;198    fn set_collection_sponsor() -> Weight;199    fn confirm_sponsorship() -> Weight;200    fn remove_collection_sponsor() -> Weight;201    fn create_item(s: usize) -> Weight;202    fn burn_item() -> Weight;203    fn transfer() -> Weight;204    fn approve() -> Weight;205    fn transfer_from() -> Weight;206    fn set_offchain_schema() -> Weight;207    fn set_const_on_chain_schema() -> Weight;208    fn set_variable_on_chain_schema() -> Weight;209    fn set_variable_meta_data() -> Weight;210    // fn enable_contract_sponsoring() -> Weight;211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateNftData {216    pub const_data: Vec<u8>,217    pub variable_data: Vec<u8>,218}219220#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]221#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]222pub struct CreateFungibleData {223}224225#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub struct CreateReFungibleData {228    pub const_data: Vec<u8>,229    pub variable_data: Vec<u8>,230}231232#[derive(Encode, Decode, Debug, Clone, PartialEq)]233#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]234pub enum CreateItemData {235    NFT(CreateNftData),236    Fungible(CreateFungibleData),237    ReFungible(CreateReFungibleData)238}239240impl CreateItemData {241    pub fn len(&self) -> usize {242        let len = match self {243            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),244            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),245            _ => 0246        };247        248        return len;249    }250}251252impl From<CreateNftData> for CreateItemData {253    fn from(item: CreateNftData) -> Self {254        CreateItemData::NFT(item)255    }256}257258impl From<CreateReFungibleData> for CreateItemData {259    fn from(item: CreateReFungibleData) -> Self {260        CreateItemData::ReFungible(item)261    }262}263264impl From<CreateFungibleData> for CreateItemData {265    fn from(item: CreateFungibleData) -> Self {266        CreateItemData::Fungible(item)267    }268}269270pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {271    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;272273    /// Weight information for extrinsics in this pallet.274	type WeightInfo: WeightInfo;275}276277#[cfg(feature = "runtime-benchmarks")]278mod benchmarking;279280// #endregion281282decl_storage! {283    trait Store for Module<T: Trait> as Nft {284285        // Private members286        NextCollectionID: u64;287        CreatedCollectionCount: u64;288        ChainVersion: u64;289        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;290291        // Chain limits struct292        pub ChainLimit get(fn chain_limit) config(): ChainLimits;293294        // Bound counters295        CollectionCount: u64;296        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;297298        // Basic collections299        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;300        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;301        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;302303        /// Balance owner per collection map304        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;305306        /// second parameter: item id + owner account id307        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;308309        /// Item collections310        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;311        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;312        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;313314        /// Index list315        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;316317        /// Tokens transfer baskets318        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;319        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>>;320        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;321322        // Contract Sponsorship and Ownership323        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;324        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;325    }326    add_extra_genesis {327        build(|config: &GenesisConfig<T>| {328            // Modification of storage329            for (_num, _c) in &config.collection {330                <Module<T>>::init_collection(_c);331            }332333            for (_num, _q, _i) in &config.nft_item_id {334                <Module<T>>::init_nft_token(_i);335            }336337            for (_num, _q, _i) in &config.fungible_item_id {338                <Module<T>>::init_fungible_token(_i);339            }340341            for (_num, _q, _i) in &config.refungible_item_id {342                <Module<T>>::init_refungible_token(_i);343            }344        })345    }346}347348decl_event!(349    pub enum Event<T>350    where351        AccountId = <T as system::Trait>::AccountId,352    {353        /// New collection was created354        /// 355        /// # Arguments356        /// 357        /// * collection_id: Globally unique identifier of newly created collection.358        /// 359        /// * mode: [CollectionMode] converted into u8.360        /// 361        /// * account_id: Collection owner.362        Created(u64, u8, AccountId),363364        /// New item was created.365        /// 366        /// # Arguments367        /// 368        /// * collection_id: Id of the collection where item was created.369        /// 370        /// * item_id: Id of an item. Unique within the collection.371        ItemCreated(u64, u64),372373        /// Collection item was burned.374        /// 375        /// # Arguments376        /// 377        /// collection_id.378        /// 379        /// item_id: Identifier of burned NFT.380        ItemDestroyed(u64, u64),381    }382);383384decl_module! {385    pub struct Module<T: Trait> for enum Call where origin: T::Origin {386387        fn deposit_event() = default;388389        fn on_initialize(now: T::BlockNumber) -> Weight {390391            if ChainVersion::get() < 2392            {393                let value = NextCollectionID::get();394                CreatedCollectionCount::put(value);395                ChainVersion::put(2);396            }397398            0399        }400401        /// 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.402        /// 403        /// # Permissions404        /// 405        /// * Anyone.406        /// 407        /// # Arguments408        /// 409        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.410        /// 411        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.412        /// 413        /// * token_prefix: UTF-8 string with token prefix.414        /// 415        /// * mode: [CollectionMode] collection type and type dependent data.416        // returns collection ID417        #[weight = T::WeightInfo::create_collection()]418        pub fn create_collection(origin,419                                 collection_name: Vec<u16>,420                                 collection_description: Vec<u16>,421                                 token_prefix: Vec<u8>,422                                 mode: CollectionMode) -> DispatchResult {423424            // Anyone can create a collection425            let who = ensure_signed(origin)?;426427            let decimal_points = match mode {428                CollectionMode::Fungible(points) => points,429                CollectionMode::ReFungible(points) => points,430                _ => 0431            };432433            // bound Total number of collections434            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");435436            // check params437            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");438439            let mut name = collection_name.to_vec();440            name.push(0);441            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");442443            let mut description = collection_description.to_vec();444            description.push(0);445            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");446447            let mut prefix = token_prefix.to_vec();448            prefix.push(0);449            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");450451            // Generate next collection ID452            let next_id = CreatedCollectionCount::get()453                .checked_add(1)454                .expect("collection id error");455456            // bound counter457            let total = CollectionCount::get()458                .checked_add(1)459                .expect("collection counter error");460461            CreatedCollectionCount::put(next_id);462            CollectionCount::put(total);463464            // Create new collection465            let new_collection = CollectionType {466                owner: who.clone(),467                name: name,468                mode: mode.clone(),469                mint_mode: false,470                access: AccessMode::Normal,471                description: description,472                decimal_points: decimal_points,473                token_prefix: prefix,474                offchain_schema: Vec::new(),475                sponsor: T::AccountId::default(),476                unconfirmed_sponsor: T::AccountId::default(),477                variable_on_chain_schema: Vec::new(),478                const_on_chain_schema: Vec::new(),479            };480481            // Add new collection to map482            <Collection<T>>::insert(next_id, new_collection);483484            // call event485            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));486487            Ok(())488        }489490        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.491        /// 492        /// # Permissions493        /// 494        /// * Collection Owner.495        /// 496        /// # Arguments497        /// 498        /// * collection_id: collection to destroy.499        #[weight = T::WeightInfo::destroy_collection()]500        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {501502            let sender = ensure_signed(origin)?;503            Self::check_owner_permissions(collection_id, sender)?;504505            <AddressTokens<T>>::remove_prefix(collection_id);506            <ApprovedList<T>>::remove_prefix(collection_id);507            <Balance<T>>::remove_prefix(collection_id);508            <ItemListIndex>::remove(collection_id);509            <AdminList<T>>::remove(collection_id);510            <Collection<T>>::remove(collection_id);511            <WhiteList<T>>::remove(collection_id);512513            <NftItemList<T>>::remove_prefix(collection_id);514            <FungibleItemList<T>>::remove_prefix(collection_id);515            <ReFungibleItemList<T>>::remove_prefix(collection_id);516517            <NftTransferBasket<T>>::remove_prefix(collection_id);518            <FungibleTransferBasket<T>>::remove_prefix(collection_id);519            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);520521            if CollectionCount::get() > 0522            {523                // bound couter524                let total = CollectionCount::get()525                    .checked_sub(1)526                    .expect("collection counter error");527528                CollectionCount::put(total);529            }530531            Ok(())532        }533534        /// Add an address to white list.535        /// 536        /// # Permissions537        /// 538        /// * Collection Owner539        /// * Collection Admin540        /// 541        /// # Arguments542        /// 543        /// * collection_id.544        /// 545        /// * address.546        #[weight = T::WeightInfo::add_to_white_list()]547        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{548549            let sender = ensure_signed(origin)?;550            Self::check_owner_or_admin_permissions(collection_id, sender)?;551552            let mut white_list_collection: Vec<T::AccountId>;553            if <WhiteList<T>>::contains_key(collection_id) {554                white_list_collection = <WhiteList<T>>::get(collection_id);555                if !white_list_collection.contains(&address.clone())556                {557                    white_list_collection.push(address.clone());558                }559            }560            else {561                white_list_collection = Vec::new();562                white_list_collection.push(address.clone());563            }564565            <WhiteList<T>>::insert(collection_id, white_list_collection);566            Ok(())567        }568569        /// Remove an address from white list.570        /// 571        /// # Permissions572        /// 573        /// * Collection Owner574        /// * Collection Admin575        /// 576        /// # Arguments577        /// 578        /// * collection_id.579        /// 580        /// * address.581        #[weight = T::WeightInfo::remove_from_white_list()]582        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{583584            let sender = ensure_signed(origin)?;585            Self::check_owner_or_admin_permissions(collection_id, sender)?;586587            if <WhiteList<T>>::contains_key(collection_id) {588                let mut white_list_collection = <WhiteList<T>>::get(collection_id);589                if white_list_collection.contains(&address.clone())590                {591                    white_list_collection.retain(|i| *i != address.clone());592                    <WhiteList<T>>::insert(collection_id, white_list_collection);593                }594            }595596            Ok(())597        }598599        /// Toggle between normal and white list access for the methods with access for `Anyone`.600        /// 601        /// # Permissions602        /// 603        /// * Collection Owner.604        /// 605        /// # Arguments606        /// 607        /// * collection_id.608        /// 609        /// * mode: [AccessMode]610        #[weight = T::WeightInfo::set_public_access_mode()]611        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult612        {613            let sender = ensure_signed(origin)?;614615            Self::check_owner_permissions(collection_id, sender)?;616            let mut target_collection = <Collection<T>>::get(collection_id);617            target_collection.access = mode;618            <Collection<T>>::insert(collection_id, target_collection);619620            Ok(())621        }622623        /// Allows Anyone to create tokens if:624        /// * White List is enabled, and625        /// * Address is added to white list, and626        /// * This method was called with True parameter627        /// 628        /// # Permissions629        /// * Collection Owner630        ///631        /// # Arguments632        /// 633        /// * collection_id.634        /// 635        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.636        #[weight = T::WeightInfo::set_mint_permission()]637        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult638        {639            let sender = ensure_signed(origin)?;640641            Self::check_owner_permissions(collection_id, sender)?;642            let mut target_collection = <Collection<T>>::get(collection_id);643            target_collection.mint_mode = mint_permission;644            <Collection<T>>::insert(collection_id, target_collection);645646            Ok(())647        }648649        /// Change the owner of the collection.650        /// 651        /// # Permissions652        /// 653        /// * Collection Owner.654        /// 655        /// # Arguments656        /// 657        /// * collection_id.658        /// 659        /// * new_owner.660        #[weight = T::WeightInfo::change_collection_owner()]661        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {662663            let sender = ensure_signed(origin)?;664            Self::check_owner_permissions(collection_id, sender)?;665            let mut target_collection = <Collection<T>>::get(collection_id);666            target_collection.owner = new_owner;667            <Collection<T>>::insert(collection_id, target_collection);668669            Ok(())670        }671672        /// Adds an admin of the Collection.673        /// 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. 674        /// 675        /// # Permissions676        /// 677        /// * Collection Owner.678        /// * Collection Admin.679        /// 680        /// # Arguments681        /// 682        /// * collection_id: ID of the Collection to add admin for.683        /// 684        /// * new_admin_id: Address of new admin to add.685        #[weight = T::WeightInfo::add_collection_admin()]686        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {687688            let sender = ensure_signed(origin)?;689            Self::check_owner_or_admin_permissions(collection_id, sender)?;690            let mut admin_arr: Vec<T::AccountId> = Vec::new();691692            if <AdminList<T>>::contains_key(collection_id)693            {694                admin_arr = <AdminList<T>>::get(collection_id);695                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");696            }697698            // Number of collection admins699            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");700701            admin_arr.push(new_admin_id);702            <AdminList<T>>::insert(collection_id, admin_arr);703704            Ok(())705        }706707        /// 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.708        ///709        /// # Permissions710        /// 711        /// * Collection Owner.712        /// * Collection Admin.713        /// 714        /// # Arguments715        /// 716        /// * collection_id: ID of the Collection to remove admin for.717        /// 718        /// * account_id: Address of admin to remove.719        #[weight = T::WeightInfo::remove_collection_admin()]720        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {721722            let sender = ensure_signed(origin)?;723            Self::check_owner_or_admin_permissions(collection_id, sender)?;724725            if <AdminList<T>>::contains_key(collection_id)726            {727                let mut admin_arr = <AdminList<T>>::get(collection_id);728                admin_arr.retain(|i| *i != account_id);729                <AdminList<T>>::insert(collection_id, admin_arr);730            }731732            Ok(())733        }734735        /// # Permissions736        /// 737        /// * Collection Owner738        /// 739        /// # Arguments740        /// 741        /// * collection_id.742        /// 743        /// * new_sponsor.744        #[weight = T::WeightInfo::set_collection_sponsor()]745        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> 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.unconfirmed_sponsor = new_sponsor;754            <Collection<T>>::insert(collection_id, target_collection);755756            Ok(())757        }758759        /// # Permissions760        /// 761        /// * Sponsor.762        /// 763        /// # Arguments764        /// 765        /// * collection_id.766        #[weight = T::WeightInfo::confirm_sponsorship()]767        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {768769            let sender = ensure_signed(origin)?;770            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");771772            let mut target_collection = <Collection<T>>::get(collection_id);773            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");774775            target_collection.sponsor = target_collection.unconfirmed_sponsor;776            target_collection.unconfirmed_sponsor = T::AccountId::default();777            <Collection<T>>::insert(collection_id, target_collection);778779            Ok(())780        }781782        /// Switch back to pay-per-own-transaction model.783        ///784        /// # Permissions785        ///786        /// * Collection owner.787        /// 788        /// # Arguments789        /// 790        /// * collection_id.791        #[weight = T::WeightInfo::remove_collection_sponsor()]792        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {793794            let sender = ensure_signed(origin)?;795            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");796797            let mut target_collection = <Collection<T>>::get(collection_id);798            ensure!(sender == target_collection.owner, "You do not own this collection");799800            target_collection.sponsor = T::AccountId::default();801            <Collection<T>>::insert(collection_id, target_collection);802803            Ok(())804        }805806        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.807        /// 808        /// # Permissions809        /// 810        /// * Collection Owner.811        /// * Collection Admin.812        /// * Anyone if813        ///     * White List is enabled, and814        ///     * Address is added to white list, and815        ///     * MintPermission is enabled (see SetMintPermission method)816        /// 817        /// # Arguments818        /// 819        /// * collection_id: ID of the collection.820        /// 821        /// * owner: Address, initial owner of the NFT.822        ///823        /// * data: Token data to store on chain.824        // #[weight =825        // (130_000_000 as Weight)826        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))827        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))828        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]829830        #[weight = T::WeightInfo::create_item(data.len())]831        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {832833            let sender = ensure_signed(origin)?;834835            Self::collection_exists(collection_id)?;836837            let target_collection = <Collection<T>>::get(collection_id);838839            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;840            Self::validate_create_item_args(&target_collection, &data)?;841            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;842843            Ok(())844        }845846        /// This method creates multiple instances of NFT Collection created with CreateCollection method.847        /// 848        /// # Permissions849        /// 850        /// * Collection Owner.851        /// * Collection Admin.852        /// * Anyone if853        ///     * White List is enabled, and854        ///     * Address is added to white list, and855        ///     * MintPermission is enabled (see SetMintPermission method)856        /// 857        /// # Arguments858        /// 859        /// * collection_id: ID of the collection.860        /// 861        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].862        /// 863        /// * owner: Address, initial owner of the NFT.864        #[weight = T::WeightInfo::create_item(items_data.into_iter()865                               .map(|data| { data.len() })866                               .sum())]867        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {868869            ensure!(items_data.len() > 0, "Length of items properties must be greater than 0.");870            let sender = ensure_signed(origin)?;871872            Self::collection_exists(collection_id)?;873            let target_collection = <Collection<T>>::get(collection_id);874875            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;876877            for data in &items_data {878                Self::validate_create_item_args(&target_collection, data)?;879            }880            for data in &items_data {881                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;882            }883884            Ok(())885        }886887        /// Destroys a concrete instance of NFT.888        /// 889        /// # Permissions890        /// 891        /// * Collection Owner.892        /// * Collection Admin.893        /// * Current NFT Owner.894        /// 895        /// # Arguments896        /// 897        /// * collection_id: ID of the collection.898        /// 899        /// * item_id: ID of NFT to burn.900        #[weight = T::WeightInfo::burn_item()]901        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {902903            let sender = ensure_signed(origin)?;904            Self::collection_exists(collection_id)?;905906            // Transfer permissions check907            let target_collection = <Collection<T>>::get(collection_id);908            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||909                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),910                "Only item owner, collection owner and admins can modify item");911912            if target_collection.access == AccessMode::WhiteList {913                Self::check_white_list(collection_id, &sender)?;914            }915916            match target_collection.mode917            {918                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,919                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,920                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,921                _ => ()922            };923924            // call event925            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));926927            Ok(())928        }929930        /// Change ownership of the token.931        /// 932        /// # Permissions933        /// 934        /// * Collection Owner935        /// * Collection Admin936        /// * Current NFT owner937        ///938        /// # Arguments939        /// 940        /// * recipient: Address of token recipient.941        /// 942        /// * collection_id.943        /// 944        /// * item_id: ID of the item945        ///     * Non-Fungible Mode: Required.946        ///     * Fungible Mode: Ignored.947        ///     * Re-Fungible Mode: Required.948        /// 949        /// * value: Amount to transfer.950        ///     * Non-Fungible Mode: Ignored951        ///     * Fungible Mode: Must specify transferred amount952        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)953        #[weight = T::WeightInfo::transfer()]954        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {955956            let sender = ensure_signed(origin)?;957958            // Transfer permissions check959            let target_collection = <Collection<T>>::get(collection_id);960            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||961                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),962                "Only item owner, collection owner and admins can modify item");963964            if target_collection.access == AccessMode::WhiteList {965                Self::check_white_list(collection_id, &sender)?;966                Self::check_white_list(collection_id, &recipient)?;967            }968969            match target_collection.mode970            {971                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,972                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,973                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,974                _ => ()975            };976977            Ok(())978        }979980        /// Set, change, or remove approved address to transfer the ownership of the NFT.981        /// 982        /// # Permissions983        /// 984        /// * Collection Owner985        /// * Collection Admin986        /// * Current NFT owner987        /// 988        /// # Arguments989        /// 990        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).991        /// 992        /// * collection_id.993        /// 994        /// * item_id: ID of the item.995        #[weight = T::WeightInfo::approve()]996        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {997998            let sender = ensure_signed(origin)?;9991000            // Transfer permissions check1001            let target_collection = <Collection<T>>::get(collection_id);1002            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1003                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1004                "Only item owner, collection owner and admins can approve");10051006            if target_collection.access == AccessMode::WhiteList {1007                Self::check_white_list(collection_id, &sender)?;1008                Self::check_white_list(collection_id, &approved)?;1009            }10101011            // amount param stub1012            let amount = 100000000;10131014            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1015            if list_exists {10161017                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1018                let item_contains = list.iter().any(|i| i.approved == approved);10191020                if !item_contains {1021                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1022                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1023                }1024            } else {10251026                let mut list = Vec::new();1027                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1028                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1029            }10301031            Ok(())1032        }1033        1034        /// 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.1035        /// 1036        /// # Permissions1037        /// * Collection Owner1038        /// * Collection Admin1039        /// * Current NFT owner1040        /// * Address approved by current NFT owner1041        /// 1042        /// # Arguments1043        /// 1044        /// * from: Address that owns token.1045        /// 1046        /// * recipient: Address of token recipient.1047        /// 1048        /// * collection_id.1049        /// 1050        /// * item_id: ID of the item.1051        /// 1052        /// * value: Amount to transfer.1053        #[weight = T::WeightInfo::transfer_from()]1054        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10551056            let sender = ensure_signed(origin)?;1057            let mut appoved_transfer = false;10581059            // Check approve1060            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1061                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1062                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1063                if opt_item.is_some()1064                {1065                    appoved_transfer = true;1066                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1067                }1068            }10691070            // Transfer permissions check1071            let target_collection = <Collection<T>>::get(collection_id);1072            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1073                "Only item owner, collection owner and admins can modify items");10741075            if target_collection.access == AccessMode::WhiteList {1076                Self::check_white_list(collection_id, &sender)?;1077                Self::check_white_list(collection_id, &recipient)?;1078            }10791080            // remove approve1081            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1082                .into_iter().filter(|i| i.approved != sender.clone()).collect();1083            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);108410851086            match target_collection.mode1087            {1088                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1089                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1090                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1091                _ => ()1092            };10931094            Ok(())1095        }10961097        ///1098        #[weight = 0]1099        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {11001101            // let no_perm_mes = "You do not have permissions to modify this collection";1102            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1103            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1104            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);11051106            // // on_nft_received  call11071108            // Self::transfer(origin, collection_id, item_id, new_owner)?;11091110            Ok(())1111        }11121113        /// Set off-chain data schema.1114        /// 1115        /// # Permissions1116        /// 1117        /// * Collection Owner1118        /// * Collection Admin1119        /// 1120        /// # Arguments1121        /// 1122        /// * collection_id.1123        /// 1124        /// * schema: String representing the offchain data schema.1125        #[weight = T::WeightInfo::set_variable_meta_data()]1126        pub fn set_variable_meta_data (1127            origin,1128            collection_id: u64,1129            item_id: u64,1130            data: Vec<u8>1131        ) -> DispatchResult {1132            let sender = ensure_signed(origin)?;1133            1134            Self::collection_exists(collection_id)?;11351136            // Modify permissions check1137            let target_collection = <Collection<T>>::get(collection_id);1138            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1139                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1140                "Only item owner, collection owner and admins can modify item");11411142            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11431144            match target_collection.mode1145            {1146                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1147                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1148                _ => ()1149            };11501151            Ok(())1152        }1153        11541155        /// Set off-chain data schema.1156        /// 1157        /// # Permissions1158        /// 1159        /// * Collection Owner1160        /// * Collection Admin1161        /// 1162        /// # Arguments1163        /// 1164        /// * collection_id.1165        /// 1166        /// * schema: String representing the offchain data schema.1167        #[weight = T::WeightInfo::set_offchain_schema()]1168        pub fn set_offchain_schema(1169            origin,1170            collection_id: u64,1171            schema: Vec<u8>1172        ) -> DispatchResult {1173            let sender = ensure_signed(origin)?;1174            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11751176            let mut target_collection = <Collection<T>>::get(collection_id);1177            target_collection.offchain_schema = schema;1178            <Collection<T>>::insert(collection_id, target_collection);11791180            Ok(())1181        }11821183        /// Set const on-chain data schema.1184        /// 1185        /// # Permissions1186        /// 1187        /// * Collection Owner1188        /// * Collection Admin1189        /// 1190        /// # Arguments1191        /// 1192        /// * collection_id.1193        /// 1194        /// * schema: String representing the const on-chain data schema.1195        #[weight = T::WeightInfo::set_const_on_chain_schema()]1196        pub fn set_const_on_chain_schema (1197            origin,1198            collection_id: u64,1199            schema: Vec<u8>1200        ) -> DispatchResult {1201            let sender = ensure_signed(origin)?;1202            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12031204            let mut target_collection = <Collection<T>>::get(collection_id);1205            target_collection.const_on_chain_schema = schema;1206            <Collection<T>>::insert(collection_id, target_collection);12071208            Ok(())1209        }12101211        /// Set variable on-chain data schema.1212        /// 1213        /// # Permissions1214        /// 1215        /// * Collection Owner1216        /// * Collection Admin1217        /// 1218        /// # Arguments1219        /// 1220        /// * collection_id.1221        /// 1222        /// * schema: String representing the variable on-chain data schema.1223        #[weight = T::WeightInfo::set_const_on_chain_schema()]1224        pub fn set_variable_on_chain_schema (1225            origin,1226            collection_id: u64,1227            schema: Vec<u8>1228        ) -> DispatchResult {1229            let sender = ensure_signed(origin)?;1230            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12311232            let mut target_collection = <Collection<T>>::get(collection_id);1233            target_collection.variable_on_chain_schema = schema;1234            <Collection<T>>::insert(collection_id, target_collection);12351236            Ok(())1237        }12381239        // Sudo permissions function1240        #[weight = 0]1241        pub fn set_chain_limits(1242            origin,1243            limits: ChainLimits1244        ) -> DispatchResult {1245            ensure_root(origin)?;1246            <ChainLimit>::put(limits);1247            Ok(())1248        }12491250        /// Enable smart contract self-sponsoring.1251        /// 1252        /// # Permissions1253        /// 1254        /// * Contract Owner1255        /// 1256        /// # Arguments1257        /// 1258        /// * contract address1259        /// * enable flag1260        /// 1261        #[weight = 0]1262        pub fn enable_contract_sponsoring(1263            origin,1264            contract_address: T::AccountId,1265            enable: bool1266        ) -> DispatchResult {1267            let sender = ensure_signed(origin)?;1268            let mut is_owner = false;1269            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1270                let owner = <ContractOwner<T>>::get(&contract_address);1271                is_owner = sender == owner;1272            }1273            ensure!(is_owner, "Only contract owner may call this method");12741275            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1276            Ok(())1277        }12781279    }1280}12811282impl<T: Trait> Module<T> {12831284    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {12851286        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1287            ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1288            Self::check_white_list(collection_id, owner)?;1289            Self::check_white_list(collection_id, sender)?;1290        }12911292        Ok(())1293    }12941295    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1296        match target_collection.mode1297        {1298            CollectionMode::NFT => {1299                if let CreateItemData::NFT(data) = data {1300                    // check sizes1301                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1302                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1303                } else {1304                    fail!("Not NFT item data used to mint in NFT collection.");1305                }1306            },1307            CollectionMode::Fungible(_) => {1308                if let CreateItemData::Fungible(_) = data {1309                } else {1310                    fail!("Not Fungible item data used to mint in Fungible collection.");1311                }1312            },1313            CollectionMode::ReFungible(_) => {1314                if let CreateItemData::ReFungible(data) = data {13151316                    // check sizes1317                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1318                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1319                } else {1320                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1321                }1322            },1323            _ => { fail!("Unexpected collection type."); }1324        };13251326        Ok(())1327    }13281329    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1330        match data1331        {1332            CreateItemData::NFT(data) => {1333                let item = NftItemType {1334                    collection: collection_id,1335                    owner,1336                    const_data: data.const_data,1337                    variable_data: data.variable_data1338                };13391340                Self::add_nft_item(item)?;1341            },1342            CreateItemData::Fungible(_) => {1343                let item = FungibleItemType {1344                    collection: collection_id,1345                    owner,1346                    value: (10 as u128).pow(collection.decimal_points)1347                };13481349                Self::add_fungible_item(item)?;1350            },1351            CreateItemData::ReFungible(data) => {1352                let mut owner_list = Vec::new();1353                let value = (10 as u128).pow(collection.decimal_points);1354                owner_list.push(Ownership {owner: owner.clone(), fraction: value});13551356                let item = ReFungibleItemType {1357                    collection: collection_id,1358                    owner: owner_list,1359                    const_data: data.const_data,1360                    variable_data: data.variable_data1361                };13621363                Self::add_refungible_item(item)?;1364            }1365        };136613671368        // call event1369        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));13701371        Ok(())1372    }13731374    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1375        let current_index = <ItemListIndex>::get(item.collection)1376            .checked_add(1)1377            .expect("Item list index id error");1378        let itemcopy = item.clone();1379        let owner = item.owner.clone();1380        let value = item.value as u64;13811382        Self::add_token_index(item.collection, current_index, owner.clone())?;13831384        <ItemListIndex>::insert(item.collection, current_index);1385        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13861387        // Add current block1388        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1389        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1390        1391        // Update balance1392        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1393            .checked_add(value)1394            .unwrap();1395        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13961397        Ok(())1398    }13991400    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1401        let current_index = <ItemListIndex>::get(item.collection)1402            .checked_add(1)1403            .expect("Item list index id error");1404        let itemcopy = item.clone();14051406        let value = item.owner.first().unwrap().fraction as u64;1407        let owner = item.owner.first().unwrap().owner.clone();14081409        Self::add_token_index(item.collection, current_index, owner.clone())?;14101411        <ItemListIndex>::insert(item.collection, current_index);1412        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14131414        // Add current block1415        let block_number: T::BlockNumber = 0.into();1416        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14171418        // Update balance1419        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1420            .checked_add(value)1421            .unwrap();1422        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14231424        Ok(())1425    }14261427    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1428        let current_index = <ItemListIndex>::get(item.collection)1429            .checked_add(1)1430            .expect("Item list index id error");14311432        let item_owner = item.owner.clone();1433        let collection_id = item.collection.clone();1434        Self::add_token_index(collection_id, current_index, item.owner.clone())?;14351436        <ItemListIndex>::insert(collection_id, current_index);1437        <NftItemList<T>>::insert(collection_id, current_index, item);14381439        // Add current block1440        let block_number: T::BlockNumber = 0.into();1441        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14421443        // Update balance1444        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1445            .checked_add(1)1446            .unwrap();1447        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14481449        Ok(())1450    }14511452    fn burn_refungible_item(1453        collection_id: u64,1454        item_id: u64,1455        owner: T::AccountId,1456    ) -> DispatchResult {1457        ensure!(1458            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1459            "Item does not exists"1460        );1461        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1462        let item = collection1463            .owner1464            .iter()1465            .filter(|&i| i.owner == owner)1466            .next()1467            .unwrap();1468        Self::remove_token_index(collection_id, item_id, owner.clone())?;14691470        // remove approve list1471        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14721473        // update balance1474        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1475            .checked_sub(item.fraction as u64)1476            .unwrap();1477        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14781479        <ReFungibleItemList<T>>::remove(collection_id, item_id);14801481        Ok(())1482    }14831484    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1485        ensure!(1486            <NftItemList<T>>::contains_key(collection_id, item_id),1487            "Item does not exists"1488        );1489        let item = <NftItemList<T>>::get(collection_id, item_id);1490        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14911492        // remove approve list1493        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14941495        // update balance1496        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1497            .checked_sub(1)1498            .unwrap();1499        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1500        <NftItemList<T>>::remove(collection_id, item_id);15011502        Ok(())1503    }15041505    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1506        ensure!(1507            <FungibleItemList<T>>::contains_key(collection_id, item_id),1508            "Item does not exists"1509        );1510        let item = <FungibleItemList<T>>::get(collection_id, item_id);1511        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15121513        // remove approve list1514        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15151516        // update balance1517        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1518            .checked_sub(item.value as u64)1519            .unwrap();1520        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15211522        <FungibleItemList<T>>::remove(collection_id, item_id);15231524        Ok(())1525    }15261527    fn collection_exists(collection_id: u64) -> DispatchResult {1528        ensure!(1529            <Collection<T>>::contains_key(collection_id),1530            "This collection does not exist"1531        );1532        Ok(())1533    }15341535    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1536        Self::collection_exists(collection_id)?;15371538        let target_collection = <Collection<T>>::get(collection_id);1539        ensure!(1540            subject == target_collection.owner,1541            "You do not own this collection"1542        );15431544        Ok(())1545    }15461547    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1548        let target_collection = <Collection<T>>::get(collection_id);1549        let mut result: bool = subject == target_collection.owner;1550        let exists = <AdminList<T>>::contains_key(collection_id);15511552        if !result & exists {1553            if <AdminList<T>>::get(collection_id).contains(&subject) {1554                result = true1555            }1556        }15571558        result1559    }15601561    fn check_owner_or_admin_permissions(1562        collection_id: u64,1563        subject: T::AccountId,1564    ) -> DispatchResult {1565        Self::collection_exists(collection_id)?;1566        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15671568        ensure!(1569            result,1570            "You do not have permissions to modify this collection"1571        );1572        Ok(())1573    }15741575    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1576        let target_collection = <Collection<T>>::get(collection_id);15771578        match target_collection.mode {1579            CollectionMode::NFT => {1580                <NftItemList<T>>::get(collection_id, item_id).owner == subject1581            }1582            CollectionMode::Fungible(_) => {1583                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1584            }1585            CollectionMode::ReFungible(_) => {1586                <ReFungibleItemList<T>>::get(collection_id, item_id)1587                    .owner1588                    .iter()1589                    .any(|i| i.owner == subject)1590            }1591            CollectionMode::Invalid => false,1592        }1593    }15941595    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1596        let mes = "Address is not in white list";1597        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1598        let wl = <WhiteList<T>>::get(collection_id);1599        ensure!(wl.contains(address), mes);16001601        Ok(())1602    }16031604    fn transfer_fungible(1605        collection_id: u64,1606        item_id: u64,1607        value: u64,1608        owner: T::AccountId,1609        new_owner: T::AccountId,1610    ) -> DispatchResult {1611        ensure!(1612            <FungibleItemList<T>>::contains_key(collection_id, item_id),1613            "Item not exists"1614        );16151616        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1617        let amount = full_item.value;16181619        ensure!(amount >= value.into(), "Item balance not enouth");16201621        // update balance1622        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1623            .checked_sub(value)1624            .unwrap();1625        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16261627        let mut new_owner_account_id = 0;1628        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1629        if new_owner_items.len() > 0 {1630            new_owner_account_id = new_owner_items[0];1631        }16321633        let val64 = value.into();16341635        // transfer1636        if amount == val64 && new_owner_account_id == 0 {1637            // change owner1638            // new owner do not have account1639            let mut new_full_item = full_item.clone();1640            new_full_item.owner = new_owner.clone();1641            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16421643            // update balance1644            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1645                .checked_add(value)1646                .unwrap();1647            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16481649            // update index collection1650            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1651        } else {1652            let mut new_full_item = full_item.clone();1653            new_full_item.value -= val64;16541655            // separate amount1656            if new_owner_account_id > 0 {1657                // new owner has account1658                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1659                item.value += val64;16601661                // update balance1662                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1663                    .checked_add(value)1664                    .unwrap();1665                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16661667                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1668            } else {1669                // new owner do not have account1670                let item = FungibleItemType {1671                    collection: collection_id,1672                    owner: new_owner.clone(),1673                    value: val64,1674                };16751676                Self::add_fungible_item(item)?;1677            }16781679            if amount == val64 {1680                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16811682                // remove approve list1683                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1684                <FungibleItemList<T>>::remove(collection_id, item_id);1685            }16861687            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1688        }16891690        Ok(())1691    }16921693    fn transfer_refungible(1694        collection_id: u64,1695        item_id: u64,1696        value: u64,1697        owner: T::AccountId,1698        new_owner: T::AccountId,1699    ) -> DispatchResult {1700        ensure!(1701            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1702            "Item not exists"1703        );17041705        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1706        let item = full_item1707            .owner1708            .iter()1709            .filter(|i| i.owner == owner)1710            .next()1711            .unwrap();1712        let amount = item.fraction;17131714        ensure!(amount >= value.into(), "Item balance not enouth");17151716        // update balance1717        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1718            .checked_sub(value)1719            .unwrap();1720        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17211722        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1723            .checked_add(value)1724            .unwrap();1725        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17261727        let old_owner = item.owner.clone();1728        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1729        let val64 = value.into();17301731        // transfer1732        if amount == val64 && !new_owner_has_account {1733            // change owner1734            // new owner do not have account1735            let mut new_full_item = full_item.clone();1736            new_full_item1737                .owner1738                .iter_mut()1739                .find(|i| i.owner == owner)1740                .unwrap()1741                .owner = new_owner.clone();1742            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17431744            // update index collection1745            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1746        } else {1747            let mut new_full_item = full_item.clone();1748            new_full_item1749                .owner1750                .iter_mut()1751                .find(|i| i.owner == owner)1752                .unwrap()1753                .fraction -= val64;17541755            // separate amount1756            if new_owner_has_account {1757                // new owner has account1758                new_full_item1759                    .owner1760                    .iter_mut()1761                    .find(|i| i.owner == new_owner)1762                    .unwrap()1763                    .fraction += val64;1764            } else {1765                // new owner do not have account1766                new_full_item.owner.push(Ownership {1767                    owner: new_owner.clone(),1768                    fraction: val64,1769                });1770                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1771            }17721773            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1774        }17751776        Ok(())1777    }17781779    fn transfer_nft(1780        collection_id: u64,1781        item_id: u64,1782        sender: T::AccountId,1783        new_owner: T::AccountId,1784    ) -> DispatchResult {1785        ensure!(1786            <NftItemList<T>>::contains_key(collection_id, item_id),1787            "Item not exists"1788        );17891790        let mut item = <NftItemList<T>>::get(collection_id, item_id);17911792        ensure!(1793            sender == item.owner,1794            "sender parameter and item owner must be equal"1795        );17961797        // update balance1798        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1799            .checked_sub(1)1800            .unwrap();1801        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);18021803        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1804            .checked_add(1)1805            .unwrap();1806        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18071808        // change owner1809        let old_owner = item.owner.clone();1810        item.owner = new_owner.clone();1811        <NftItemList<T>>::insert(collection_id, item_id, item);18121813        // update index collection1814        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18151816        // reset approved list1817        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1818        Ok(())1819    }1820    1821    fn item_exists(1822        collection_id: u64,1823        item_id: u64,1824        mode: &CollectionMode1825    ) -> DispatchResult {1826        match mode {1827            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1828            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1829            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1830            _ => ()1831        };1832        1833        Ok(())1834    }18351836    fn set_re_fungible_variable_data(1837        collection_id: u64,1838        item_id: u64,1839        data: Vec<u8>1840    ) -> DispatchResult {1841        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18421843        item.variable_data = data;18441845        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18461847        Ok(())1848    }18491850    fn set_nft_variable_data(1851        collection_id: u64,1852        item_id: u64,1853        data: Vec<u8>1854    ) -> DispatchResult {1855        let mut item = <NftItemList<T>>::get(collection_id, item_id);1856        1857        item.variable_data = data;18581859        <NftItemList<T>>::insert(collection_id, item_id, item);1860        1861        Ok(())1862    }18631864    fn init_collection(item: &CollectionType<T::AccountId>) {1865        // check params1866        assert!(1867            item.decimal_points <= 4,1868            "decimal_points parameter must be lower than 4"1869        );1870        assert!(1871            item.name.len() <= 64,1872            "Collection name can not be longer than 63 char"1873        );1874        assert!(1875            item.name.len() <= 256,1876            "Collection description can not be longer than 255 char"1877        );1878        assert!(1879            item.token_prefix.len() <= 16,1880            "Token prefix can not be longer than 15 char"1881        );18821883        // Generate next collection ID1884        let next_id = CreatedCollectionCount::get()1885            .checked_add(1)1886            .expect("collection id error");18871888        CreatedCollectionCount::put(next_id);1889    }18901891    fn init_nft_token(item: &NftItemType<T::AccountId>) {1892        let current_index = <ItemListIndex>::get(item.collection)1893            .checked_add(1)1894            .expect("Item list index id error");18951896        let item_owner = item.owner.clone();1897        let collection_id = item.collection.clone();1898        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18991900        <ItemListIndex>::insert(collection_id, current_index);19011902        // Update balance1903        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1904            .checked_add(1)1905            .unwrap();1906        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1907    }19081909    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1910        let current_index = <ItemListIndex>::get(item.collection)1911            .checked_add(1)1912            .expect("Item list index id error");1913        let owner = item.owner.clone();1914        let value = item.value as u64;19151916        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19171918        <ItemListIndex>::insert(item.collection, current_index);19191920        // Update balance1921        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1922            .checked_add(value)1923            .unwrap();1924        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1925    }19261927    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1928        let current_index = <ItemListIndex>::get(item.collection)1929            .checked_add(1)1930            .expect("Item list index id error");19311932        let value = item.owner.first().unwrap().fraction as u64;1933        let owner = item.owner.first().unwrap().owner.clone();19341935        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19361937        <ItemListIndex>::insert(item.collection, current_index);19381939        // Update balance1940        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1941            .checked_add(value)1942            .unwrap();1943        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1944    }19451946    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19471948        // add to account limit1949        if <AccountItemCount<T>>::contains_key(owner.clone()) {19501951            // bound Owned tokens by a single address1952            let count = <AccountItemCount<T>>::get(owner.clone());1953            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");19541955            <AccountItemCount<T>>::insert(owner.clone(), 1956                count.checked_add(1).unwrap());1957        }1958        else {1959            <AccountItemCount<T>>::insert(owner.clone(), 1);1960        }19611962        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1963        if list_exists {1964            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1965            let item_contains = list.contains(&item_index.clone());19661967            if !item_contains {1968                list.push(item_index.clone());1969            }19701971            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1972        } else {1973            let mut itm = Vec::new();1974            itm.push(item_index.clone());1975            <AddressTokens<T>>::insert(collection_id, owner, itm);1976            1977        }19781979        Ok(())1980    }19811982    fn remove_token_index(1983        collection_id: u64,1984        item_index: u64,1985        owner: T::AccountId,1986    ) -> DispatchResult {19871988        // update counter1989        <AccountItemCount<T>>::insert(owner.clone(), 1990            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());199119921993        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1994        if list_exists {1995            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1996            let item_contains = list.contains(&item_index.clone());19971998            if item_contains {1999                list.retain(|&item| item != item_index);2000                <AddressTokens<T>>::insert(collection_id, owner, list);2001            }2002        }20032004        Ok(())2005    }20062007    fn move_token_index(2008        collection_id: u64,2009        item_index: u64,2010        old_owner: T::AccountId,2011        new_owner: T::AccountId,2012    ) -> DispatchResult {2013        Self::remove_token_index(collection_id, item_index, old_owner)?;2014        Self::add_token_index(collection_id, item_index, new_owner)?;20152016        Ok(())2017    }2018}20192020////////////////////////////////////////////////////////////////////////////////////////////////////2021// Economic models2022// #region20232024/// Fee multiplier.2025pub type Multiplier = FixedU128;20262027type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2028    <T as system::Trait>::AccountId,2029>>::Balance;2030type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2031    <T as system::Trait>::AccountId,2032>>::NegativeImbalance;20332034/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2035/// in the queue.2036#[derive(Encode, Decode, Clone, Eq, PartialEq)]2037pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2038    #[codec(compact)] BalanceOf<T>2039);20402041impl<T: Trait + Send + Sync> sp_std::fmt::Debug2042    for ChargeTransactionPayment<T>2043{2044    #[cfg(feature = "std")]2045    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2046        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2047    }2048    #[cfg(not(feature = "std"))]2049    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2050        Ok(())2051    }2052}20532054impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2055where2056    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2057    BalanceOf<T>: Send + Sync + FixedPointOperand,2058{2059    /// utility constructor. Used only in client/factory code.2060    pub fn from(fee: BalanceOf<T>) -> Self {2061        Self(fee)2062    }20632064    pub fn traditional_fee(2065        len: usize,2066        info: &DispatchInfoOf<T::Call>,2067        tip: BalanceOf<T>,2068    ) -> BalanceOf<T>2069    where2070        T::Call: Dispatchable<Info = DispatchInfo>,2071    {2072        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2073    }20742075    fn withdraw_fee(2076        &self,2077        who: &T::AccountId,2078        call: &T::Call,2079        info: &DispatchInfoOf<T::Call>,2080        len: usize,2081    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2082        let tip = self.0;20832084        // Set fee based on call type. Creating collection costs 1 Unique.2085        // All other transactions have traditional fees so far2086        // let fee = match call.is_sub_type() {2087        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2088        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2089        //                                                 // _ => <BalanceOf<T>>::from(100)2090        // };2091        let fee = Self::traditional_fee(len, info, tip);20922093        // Determine who is paying transaction fee based on ecnomic model2094        // Parse call to extract collection ID and access collection sponsor2095        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2096            Some(Call::create_item(collection_id, _properties, _owner)) => {2097                <Collection<T>>::get(collection_id).sponsor2098            }2099            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2100                let _collection_mode = <Collection<T>>::get(collection_id).mode;21012102                // sponsor timeout2103                let sponsor_transfer = match _collection_mode {2104                    CollectionMode::NFT => {2105                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2106                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2107                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2108                        if block_number >= limit_time {2109                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2110                            true2111                        }2112                        else {2113                            false2114                        }2115                    }2116                    CollectionMode::Fungible(_) => {2117                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2118                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2119                        if basket.iter().any(|i| i.address == _new_owner.clone())2120                        {2121                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2122                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2123                            if block_number >= limit_time {2124                                basket.retain(|x| x.address == item.address);2125                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2126                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2127                                true2128                            }2129                            else {2130                                false2131                            }2132                        }2133                        else {2134                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2135                            true2136                        }2137                    }2138                    CollectionMode::ReFungible(_) => {2139                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2140                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2141                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2142                        if block_number >= limit_time {2143                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2144                            true2145                        } else {2146                            false2147                        }2148                    }2149                    _ => {2150                        false2151                    },2152                };21532154                if !sponsor_transfer {2155                    T::AccountId::default()2156                } else {2157                    <Collection<T>>::get(collection_id).sponsor2158                }2159            }21602161            _ => T::AccountId::default(),2162        };21632164        // Sponsor smart contracts2165        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21662167            // On instantiation: set the contract owner2168            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21692170                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2171                    code_hash,2172                    &data,2173                    &who,2174                );2175                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21762177                T::AccountId::default()2178            },21792180            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2181            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21822183                let mut sp = T::AccountId::default();2184                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2185                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2186                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2187                        sp = called_contract;2188                    }2189                }21902191                sp2192            },21932194            _ => sponsor,2195        };21962197        let mut who_pays_fee: T::AccountId = sponsor.clone();2198        if sponsor == T::AccountId::default() {2199            who_pays_fee = who.clone();2200        }22012202        // Only mess with balances if fee is not zero.2203        if fee.is_zero() {2204            return Ok((fee, None));2205        }22062207        match <T as transaction_payment::Trait>::Currency::withdraw(2208            &who_pays_fee,2209            fee,2210            if tip.is_zero() {2211                WithdrawReason::TransactionPayment.into()2212            } else {2213                WithdrawReason::TransactionPayment | WithdrawReason::Tip2214            },2215            ExistenceRequirement::KeepAlive,2216        ) {2217            Ok(imbalance) => Ok((fee, Some(imbalance))),2218            Err(_) => Err(InvalidTransaction::Payment.into()),2219        }2220    }2221}222222232224impl<T: Trait + Send + Sync> SignedExtension2225    for ChargeTransactionPayment<T>2226where2227    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2228    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2229{2230    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2231    type AccountId = T::AccountId;2232    type Call = T::Call;2233    type AdditionalSigned = ();2234    type Pre = (2235        BalanceOf<T>,2236        Self::AccountId,2237        Option<NegativeImbalanceOf<T>>,2238        BalanceOf<T>,2239    );2240    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2241        Ok(())2242    }22432244    fn validate(2245        &self,2246        _who: &Self::AccountId,2247        _call: &Self::Call,2248        _info: &DispatchInfoOf<Self::Call>,2249        _len: usize,2250    ) -> TransactionValidity {2251        Ok(ValidTransaction::default())2252    }22532254    fn pre_dispatch(2255        self,2256        who: &Self::AccountId,2257        call: &Self::Call,2258        info: &DispatchInfoOf<Self::Call>,2259        len: usize,2260    ) -> Result<Self::Pre, TransactionValidityError> {2261        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2262        Ok((self.0, who.clone(), imbalance, fee))2263    }22642265    fn post_dispatch(2266        pre: Self::Pre,2267        info: &DispatchInfoOf<Self::Call>,2268        post_info: &PostDispatchInfoOf<Self::Call>,2269        len: usize,2270        _result: &DispatchResult,2271    ) -> Result<(), TransactionValidityError> {2272        let (tip, who, imbalance, fee) = pre;2273        if let Some(payed) = imbalance {2274            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2275                len as u32, info, post_info, tip,2276            );2277            let refund = fee.saturating_sub(actual_fee);2278            let actual_payment =2279                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2280                    &who, refund,2281                ) {2282                    Ok(refund_imbalance) => {2283                        // The refund cannot be larger than the up front payed max weight.2284                        // `PostDispatchInfo::calc_unspent` guards against such a case.2285                        match payed.offset(refund_imbalance) {2286                            Ok(actual_payment) => actual_payment,2287                            Err(_) => return Err(InvalidTransaction::Payment.into()),2288                        }2289                    }2290                    // We do not recreate the account using the refund. The up front payment2291                    // is gone in that case.2292                    Err(_) => payed,2293                };2294            let imbalances = actual_payment.split(tip);2295            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2296                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2297            );2298        }2299        Ok(())2300    }2301}23022303// #endregion
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11    construct_runtime, decl_event, decl_module, decl_storage,12    dispatch::DispatchResult,13    ensure, fail, parameter_types,14    traits::{15        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16        Randomness, WithdrawReason,17    },18    weights::{19        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21        WeightToFeePolynomial,22    },23    IsSubType, StorageValue,24};2526use frame_system::{self as system, ensure_signed, ensure_root};27use sp_runtime::sp_std::prelude::Vec;28use sp_runtime::{29    traits::{30        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SignedExtension, Zero,31    },32    transaction_validity::{33        InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,34    },35    FixedPointOperand, FixedU128,36};37use pallet_contracts::ContractAddressFor;38use sp_runtime::traits::StaticLookup;3940#[cfg(test)]41mod mock;4243#[cfg(test)]44mod tests;4546mod default_weights;4748// Structs49// #region5051#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]52#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]53pub enum CollectionMode {54    Invalid,55    NFT,56    // decimal points57    Fungible(u32),58    // decimal points59    ReFungible(u32),60}6162impl Into<u8> for CollectionMode {63    fn into(self) -> u8 {64        match self {65            CollectionMode::Invalid => 0,66            CollectionMode::NFT => 1,67            CollectionMode::Fungible(_) => 2,68            CollectionMode::ReFungible(_) => 3,69        }70    }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76    Normal,77    WhiteList,78}79impl Default for AccessMode {80    fn default() -> Self {81        Self::Normal82    }83}8485impl Default for CollectionMode {86    fn default() -> Self {87        Self::Invalid88    }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94    pub owner: AccountId,95    pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101    pub owner: AccountId,102    pub mode: CollectionMode,103    pub access: AccessMode,104    pub decimal_points: u32,105    pub name: Vec<u16>,        // 64 include null escape char106    pub description: Vec<u16>, // 256 include null escape char107    pub token_prefix: Vec<u8>, // 16 include null escape char108    pub mint_mode: bool,109    pub offchain_schema: Vec<u8>,110    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender111    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship112    pub variable_on_chain_schema: Vec<u8>, //113    pub const_on_chain_schema: Vec<u8>, //114}115116#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]117#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]118pub struct NftItemType<AccountId> {119    pub collection: u64,120    pub owner: AccountId,121    pub const_data: Vec<u8>,122    pub variable_data: Vec<u8>,123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct FungibleItemType<AccountId> {128    pub collection: u64,129    pub owner: AccountId,130    pub value: u128,131}132133#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]134#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]135pub struct ReFungibleItemType<AccountId> {136    pub collection: u64,137    pub owner: Vec<Ownership<AccountId>>,138    pub const_data: Vec<u8>,139    pub variable_data: Vec<u8>,140}141142#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]143#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]144pub struct ApprovePermissions<AccountId> {145    pub approved: AccountId,146    pub amount: u64,147}148149#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]150#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]151pub struct VestingItem<AccountId, Moment> {152    pub sender: AccountId,153    pub recipient: AccountId,154    pub collection_id: u64,155    pub item_id: u64,156    pub amount: u64,157    pub vesting_date: Moment,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct BasketItem<AccountId, BlockNumber> {163    pub address: AccountId,164    pub start_block: BlockNumber,165}166167#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct ChainLimits {170    pub collection_numbers_limit: u64,171    pub account_token_ownership_limit: u64,172    pub collections_admins_limit: u64,173    pub custom_data_limit: u32,174175    // Timeouts for item types in passed blocks176    pub nft_sponsor_transfer_timeout: u32,177    pub fungible_sponsor_transfer_timeout: u32,178    pub refungible_sponsor_transfer_timeout: u32,179}180181pub trait WeightInfo {182	fn create_collection() -> Weight;183	fn destroy_collection() -> Weight;184	fn add_to_white_list() -> Weight;185	fn remove_from_white_list() -> Weight;186    fn set_public_access_mode() -> Weight;187    fn set_mint_permission() -> Weight;188    fn change_collection_owner() -> Weight;189    fn add_collection_admin() -> Weight;190    fn remove_collection_admin() -> Weight;191    fn set_collection_sponsor() -> Weight;192    fn confirm_sponsorship() -> Weight;193    fn remove_collection_sponsor() -> Weight;194    fn create_item(s: usize) -> Weight;195    fn burn_item() -> Weight;196    fn transfer() -> Weight;197    fn approve() -> Weight;198    fn transfer_from() -> Weight;199    fn set_offchain_schema() -> Weight;200    fn set_const_on_chain_schema() -> Weight;201    fn set_variable_on_chain_schema() -> Weight;202    fn set_variable_meta_data() -> Weight;203    // fn enable_contract_sponsoring() -> Weight;204}205206#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]207#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]208pub struct CreateNftData {209    pub const_data: Vec<u8>,210    pub variable_data: Vec<u8>,211}212213#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]214#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]215pub struct CreateFungibleData {216}217218#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct CreateReFungibleData {221    pub const_data: Vec<u8>,222    pub variable_data: Vec<u8>,223}224225#[derive(Encode, Decode, Debug, Clone, PartialEq)]226#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]227pub enum CreateItemData {228    NFT(CreateNftData),229    Fungible(CreateFungibleData),230    ReFungible(CreateReFungibleData)231}232233impl CreateItemData {234    pub fn len(&self) -> usize {235        let len = match self {236            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),237            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),238            _ => 0239        };240        241        return len;242    }243}244245impl From<CreateNftData> for CreateItemData {246    fn from(item: CreateNftData) -> Self {247        CreateItemData::NFT(item)248    }249}250251impl From<CreateReFungibleData> for CreateItemData {252    fn from(item: CreateReFungibleData) -> Self {253        CreateItemData::ReFungible(item)254    }255}256257impl From<CreateFungibleData> for CreateItemData {258    fn from(item: CreateFungibleData) -> Self {259        CreateItemData::Fungible(item)260    }261}262263pub trait Trait: system::Trait + Sized + transaction_payment::Trait + pallet_contracts::Trait {264    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;265266    /// Weight information for extrinsics in this pallet.267	type WeightInfo: WeightInfo;268}269270#[cfg(feature = "runtime-benchmarks")]271mod benchmarking;272273// #endregion274275decl_storage! {276    trait Store for Module<T: Trait> as Nft {277278        // Private members279        NextCollectionID: u64;280        CreatedCollectionCount: u64;281        ChainVersion: u64;282        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;283284        // Chain limits struct285        pub ChainLimit get(fn chain_limit) config(): ChainLimits;286287        // Bound counters288        CollectionCount: u64;289        pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;290291        // Basic collections292        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;293        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;294        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;295296        /// Balance owner per collection map297        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;298299        /// second parameter: item id + owner account id300        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;301302        /// Item collections303        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;304        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;305        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;306307        /// Index list308        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;309310        /// Tokens transfer baskets311        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;312        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>>;313        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;314315        // Contract Sponsorship and Ownership316        pub ContractOwner get(fn contract_owner): map hasher(identity) T::AccountId => T::AccountId;317        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(identity) T::AccountId => bool;318    }319    add_extra_genesis {320        build(|config: &GenesisConfig<T>| {321            // Modification of storage322            for (_num, _c) in &config.collection {323                <Module<T>>::init_collection(_c);324            }325326            for (_num, _q, _i) in &config.nft_item_id {327                <Module<T>>::init_nft_token(_i);328            }329330            for (_num, _q, _i) in &config.fungible_item_id {331                <Module<T>>::init_fungible_token(_i);332            }333334            for (_num, _q, _i) in &config.refungible_item_id {335                <Module<T>>::init_refungible_token(_i);336            }337        })338    }339}340341decl_event!(342    pub enum Event<T>343    where344        AccountId = <T as system::Trait>::AccountId,345    {346        /// New collection was created347        /// 348        /// # Arguments349        /// 350        /// * collection_id: Globally unique identifier of newly created collection.351        /// 352        /// * mode: [CollectionMode] converted into u8.353        /// 354        /// * account_id: Collection owner.355        Created(u64, u8, AccountId),356357        /// New item was created.358        /// 359        /// # Arguments360        /// 361        /// * collection_id: Id of the collection where item was created.362        /// 363        /// * item_id: Id of an item. Unique within the collection.364        ItemCreated(u64, u64),365366        /// Collection item was burned.367        /// 368        /// # Arguments369        /// 370        /// collection_id.371        /// 372        /// item_id: Identifier of burned NFT.373        ItemDestroyed(u64, u64),374    }375);376377decl_module! {378    pub struct Module<T: Trait> for enum Call where origin: T::Origin {379380        fn deposit_event() = default;381382        fn on_initialize(now: T::BlockNumber) -> Weight {383384            if ChainVersion::get() < 2385            {386                let value = NextCollectionID::get();387                CreatedCollectionCount::put(value);388                ChainVersion::put(2);389            }390391            0392        }393394        /// 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.395        /// 396        /// # Permissions397        /// 398        /// * Anyone.399        /// 400        /// # Arguments401        /// 402        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.403        /// 404        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.405        /// 406        /// * token_prefix: UTF-8 string with token prefix.407        /// 408        /// * mode: [CollectionMode] collection type and type dependent data.409        // returns collection ID410        #[weight = T::WeightInfo::create_collection()]411        pub fn create_collection(origin,412                                 collection_name: Vec<u16>,413                                 collection_description: Vec<u16>,414                                 token_prefix: Vec<u8>,415                                 mode: CollectionMode) -> DispatchResult {416417            // Anyone can create a collection418            let who = ensure_signed(origin)?;419420            let decimal_points = match mode {421                CollectionMode::Fungible(points) => points,422                CollectionMode::ReFungible(points) => points,423                _ => 0424            };425426            // bound Total number of collections427            ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");428429            // check params430            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");431432            let mut name = collection_name.to_vec();433            name.push(0);434            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");435436            let mut description = collection_description.to_vec();437            description.push(0);438            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");439440            let mut prefix = token_prefix.to_vec();441            prefix.push(0);442            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");443444            // Generate next collection ID445            let next_id = CreatedCollectionCount::get()446                .checked_add(1)447                .expect("collection id error");448449            // bound counter450            let total = CollectionCount::get()451                .checked_add(1)452                .expect("collection counter error");453454            CreatedCollectionCount::put(next_id);455            CollectionCount::put(total);456457            // Create new collection458            let new_collection = CollectionType {459                owner: who.clone(),460                name: name,461                mode: mode.clone(),462                mint_mode: false,463                access: AccessMode::Normal,464                description: description,465                decimal_points: decimal_points,466                token_prefix: prefix,467                offchain_schema: Vec::new(),468                sponsor: T::AccountId::default(),469                unconfirmed_sponsor: T::AccountId::default(),470                variable_on_chain_schema: Vec::new(),471                const_on_chain_schema: Vec::new(),472            };473474            // Add new collection to map475            <Collection<T>>::insert(next_id, new_collection);476477            // call event478            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));479480            Ok(())481        }482483        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.484        /// 485        /// # Permissions486        /// 487        /// * Collection Owner.488        /// 489        /// # Arguments490        /// 491        /// * collection_id: collection to destroy.492        #[weight = T::WeightInfo::destroy_collection()]493        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {494495            let sender = ensure_signed(origin)?;496            Self::check_owner_permissions(collection_id, sender)?;497498            <AddressTokens<T>>::remove_prefix(collection_id);499            <ApprovedList<T>>::remove_prefix(collection_id);500            <Balance<T>>::remove_prefix(collection_id);501            <ItemListIndex>::remove(collection_id);502            <AdminList<T>>::remove(collection_id);503            <Collection<T>>::remove(collection_id);504            <WhiteList<T>>::remove(collection_id);505506            <NftItemList<T>>::remove_prefix(collection_id);507            <FungibleItemList<T>>::remove_prefix(collection_id);508            <ReFungibleItemList<T>>::remove_prefix(collection_id);509510            <NftTransferBasket<T>>::remove_prefix(collection_id);511            <FungibleTransferBasket<T>>::remove_prefix(collection_id);512            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);513514            if CollectionCount::get() > 0515            {516                // bound couter517                let total = CollectionCount::get()518                    .checked_sub(1)519                    .expect("collection counter error");520521                CollectionCount::put(total);522            }523524            Ok(())525        }526527        /// Add an address to white list.528        /// 529        /// # Permissions530        /// 531        /// * Collection Owner532        /// * Collection Admin533        /// 534        /// # Arguments535        /// 536        /// * collection_id.537        /// 538        /// * address.539        #[weight = T::WeightInfo::add_to_white_list()]540        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{541542            let sender = ensure_signed(origin)?;543            Self::check_owner_or_admin_permissions(collection_id, sender)?;544545            let mut white_list_collection: Vec<T::AccountId>;546            if <WhiteList<T>>::contains_key(collection_id) {547                white_list_collection = <WhiteList<T>>::get(collection_id);548                if !white_list_collection.contains(&address.clone())549                {550                    white_list_collection.push(address.clone());551                }552            }553            else {554                white_list_collection = Vec::new();555                white_list_collection.push(address.clone());556            }557558            <WhiteList<T>>::insert(collection_id, white_list_collection);559            Ok(())560        }561562        /// Remove an address from white list.563        /// 564        /// # Permissions565        /// 566        /// * Collection Owner567        /// * Collection Admin568        /// 569        /// # Arguments570        /// 571        /// * collection_id.572        /// 573        /// * address.574        #[weight = T::WeightInfo::remove_from_white_list()]575        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{576577            let sender = ensure_signed(origin)?;578            Self::check_owner_or_admin_permissions(collection_id, sender)?;579580            if <WhiteList<T>>::contains_key(collection_id) {581                let mut white_list_collection = <WhiteList<T>>::get(collection_id);582                if white_list_collection.contains(&address.clone())583                {584                    white_list_collection.retain(|i| *i != address.clone());585                    <WhiteList<T>>::insert(collection_id, white_list_collection);586                }587            }588589            Ok(())590        }591592        /// Toggle between normal and white list access for the methods with access for `Anyone`.593        /// 594        /// # Permissions595        /// 596        /// * Collection Owner.597        /// 598        /// # Arguments599        /// 600        /// * collection_id.601        /// 602        /// * mode: [AccessMode]603        #[weight = T::WeightInfo::set_public_access_mode()]604        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult605        {606            let sender = ensure_signed(origin)?;607608            Self::check_owner_permissions(collection_id, sender)?;609            let mut target_collection = <Collection<T>>::get(collection_id);610            target_collection.access = mode;611            <Collection<T>>::insert(collection_id, target_collection);612613            Ok(())614        }615616        /// Allows Anyone to create tokens if:617        /// * White List is enabled, and618        /// * Address is added to white list, and619        /// * This method was called with True parameter620        /// 621        /// # Permissions622        /// * Collection Owner623        ///624        /// # Arguments625        /// 626        /// * collection_id.627        /// 628        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.629        #[weight = T::WeightInfo::set_mint_permission()]630        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult631        {632            let sender = ensure_signed(origin)?;633634            Self::check_owner_permissions(collection_id, sender)?;635            let mut target_collection = <Collection<T>>::get(collection_id);636            target_collection.mint_mode = mint_permission;637            <Collection<T>>::insert(collection_id, target_collection);638639            Ok(())640        }641642        /// Change the owner of the collection.643        /// 644        /// # Permissions645        /// 646        /// * Collection Owner.647        /// 648        /// # Arguments649        /// 650        /// * collection_id.651        /// 652        /// * new_owner.653        #[weight = T::WeightInfo::change_collection_owner()]654        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {655656            let sender = ensure_signed(origin)?;657            Self::check_owner_permissions(collection_id, sender)?;658            let mut target_collection = <Collection<T>>::get(collection_id);659            target_collection.owner = new_owner;660            <Collection<T>>::insert(collection_id, target_collection);661662            Ok(())663        }664665        /// Adds an admin of the Collection.666        /// 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. 667        /// 668        /// # Permissions669        /// 670        /// * Collection Owner.671        /// * Collection Admin.672        /// 673        /// # Arguments674        /// 675        /// * collection_id: ID of the Collection to add admin for.676        /// 677        /// * new_admin_id: Address of new admin to add.678        #[weight = T::WeightInfo::add_collection_admin()]679        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {680681            let sender = ensure_signed(origin)?;682            Self::check_owner_or_admin_permissions(collection_id, sender)?;683            let mut admin_arr: Vec<T::AccountId> = Vec::new();684685            if <AdminList<T>>::contains_key(collection_id)686            {687                admin_arr = <AdminList<T>>::get(collection_id);688                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");689            }690691            // Number of collection admins692            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");693694            admin_arr.push(new_admin_id);695            <AdminList<T>>::insert(collection_id, admin_arr);696697            Ok(())698        }699700        /// 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.701        ///702        /// # Permissions703        /// 704        /// * Collection Owner.705        /// * Collection Admin.706        /// 707        /// # Arguments708        /// 709        /// * collection_id: ID of the Collection to remove admin for.710        /// 711        /// * account_id: Address of admin to remove.712        #[weight = T::WeightInfo::remove_collection_admin()]713        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {714715            let sender = ensure_signed(origin)?;716            Self::check_owner_or_admin_permissions(collection_id, sender)?;717718            if <AdminList<T>>::contains_key(collection_id)719            {720                let mut admin_arr = <AdminList<T>>::get(collection_id);721                admin_arr.retain(|i| *i != account_id);722                <AdminList<T>>::insert(collection_id, admin_arr);723            }724725            Ok(())726        }727728        /// # Permissions729        /// 730        /// * Collection Owner731        /// 732        /// # Arguments733        /// 734        /// * collection_id.735        /// 736        /// * new_sponsor.737        #[weight = T::WeightInfo::set_collection_sponsor()]738        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {739740            let sender = ensure_signed(origin)?;741            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");742743            let mut target_collection = <Collection<T>>::get(collection_id);744            ensure!(sender == target_collection.owner, "You do not own this collection");745746            target_collection.unconfirmed_sponsor = new_sponsor;747            <Collection<T>>::insert(collection_id, target_collection);748749            Ok(())750        }751752        /// # Permissions753        /// 754        /// * Sponsor.755        /// 756        /// # Arguments757        /// 758        /// * collection_id.759        #[weight = T::WeightInfo::confirm_sponsorship()]760        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {761762            let sender = ensure_signed(origin)?;763            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");764765            let mut target_collection = <Collection<T>>::get(collection_id);766            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");767768            target_collection.sponsor = target_collection.unconfirmed_sponsor;769            target_collection.unconfirmed_sponsor = T::AccountId::default();770            <Collection<T>>::insert(collection_id, target_collection);771772            Ok(())773        }774775        /// Switch back to pay-per-own-transaction model.776        ///777        /// # Permissions778        ///779        /// * Collection owner.780        /// 781        /// # Arguments782        /// 783        /// * collection_id.784        #[weight = T::WeightInfo::remove_collection_sponsor()]785        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {786787            let sender = ensure_signed(origin)?;788            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");789790            let mut target_collection = <Collection<T>>::get(collection_id);791            ensure!(sender == target_collection.owner, "You do not own this collection");792793            target_collection.sponsor = T::AccountId::default();794            <Collection<T>>::insert(collection_id, target_collection);795796            Ok(())797        }798799        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.800        /// 801        /// # Permissions802        /// 803        /// * Collection Owner.804        /// * Collection Admin.805        /// * Anyone if806        ///     * White List is enabled, and807        ///     * Address is added to white list, and808        ///     * MintPermission is enabled (see SetMintPermission method)809        /// 810        /// # Arguments811        /// 812        /// * collection_id: ID of the collection.813        /// 814        /// * owner: Address, initial owner of the NFT.815        ///816        /// * data: Token data to store on chain.817        // #[weight =818        // (130_000_000 as Weight)819        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))820        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))821        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]822823        #[weight = T::WeightInfo::create_item(data.len())]824        pub fn create_item(origin, collection_id: u64, owner: T::AccountId, data: CreateItemData) -> DispatchResult {825826            let sender = ensure_signed(origin)?;827828            Self::collection_exists(collection_id)?;829830            let target_collection = <Collection<T>>::get(collection_id);831832            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;833            Self::validate_create_item_args(&target_collection, &data)?;834            Self::create_item_no_validation(collection_id, &target_collection, owner, data)?;835836            Ok(())837        }838839        /// This method creates multiple instances of NFT Collection created with CreateCollection method.840        /// 841        /// # Permissions842        /// 843        /// * Collection Owner.844        /// * Collection Admin.845        /// * Anyone if846        ///     * White List is enabled, and847        ///     * Address is added to white list, and848        ///     * MintPermission is enabled (see SetMintPermission method)849        /// 850        /// # Arguments851        /// 852        /// * collection_id: ID of the collection.853        /// 854        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].855        /// 856        /// * owner: Address, initial owner of the NFT.857        #[weight = T::WeightInfo::create_item(items_data.into_iter()858                               .map(|data| { data.len() })859                               .sum())]860        pub fn create_multiple_items(origin, collection_id: u64, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {861862            ensure!(items_data.len() > 0, "Length of items properties must be greater than 0.");863            let sender = ensure_signed(origin)?;864865            Self::collection_exists(collection_id)?;866            let target_collection = <Collection<T>>::get(collection_id);867868            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;869870            for data in &items_data {871                Self::validate_create_item_args(&target_collection, data)?;872            }873            for data in &items_data {874                Self::create_item_no_validation(collection_id, &target_collection, owner.clone(), data.clone())?;875            }876877            Ok(())878        }879880        /// Destroys a concrete instance of NFT.881        /// 882        /// # Permissions883        /// 884        /// * Collection Owner.885        /// * Collection Admin.886        /// * Current NFT Owner.887        /// 888        /// # Arguments889        /// 890        /// * collection_id: ID of the collection.891        /// 892        /// * item_id: ID of NFT to burn.893        #[weight = T::WeightInfo::burn_item()]894        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {895896            let sender = ensure_signed(origin)?;897            Self::collection_exists(collection_id)?;898899            // Transfer permissions check900            let target_collection = <Collection<T>>::get(collection_id);901            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||902                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),903                "Only item owner, collection owner and admins can modify item");904905            if target_collection.access == AccessMode::WhiteList {906                Self::check_white_list(collection_id, &sender)?;907            }908909            match target_collection.mode910            {911                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,912                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,913                CollectionMode::ReFungible(_)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,914                _ => ()915            };916917            // call event918            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));919920            Ok(())921        }922923        /// Change ownership of the token.924        /// 925        /// # Permissions926        /// 927        /// * Collection Owner928        /// * Collection Admin929        /// * Current NFT owner930        ///931        /// # Arguments932        /// 933        /// * recipient: Address of token recipient.934        /// 935        /// * collection_id.936        /// 937        /// * item_id: ID of the item938        ///     * Non-Fungible Mode: Required.939        ///     * Fungible Mode: Ignored.940        ///     * Re-Fungible Mode: Required.941        /// 942        /// * value: Amount to transfer.943        ///     * Non-Fungible Mode: Ignored944        ///     * Fungible Mode: Must specify transferred amount945        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)946        #[weight = T::WeightInfo::transfer()]947        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {948949            let sender = ensure_signed(origin)?;950951            // Transfer permissions check952            let target_collection = <Collection<T>>::get(collection_id);953            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||954                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),955                "Only item owner, collection owner and admins can modify item");956957            if target_collection.access == AccessMode::WhiteList {958                Self::check_white_list(collection_id, &sender)?;959                Self::check_white_list(collection_id, &recipient)?;960            }961962            match target_collection.mode963            {964                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,965                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,966                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,967                _ => ()968            };969970            Ok(())971        }972973        /// Set, change, or remove approved address to transfer the ownership of the NFT.974        /// 975        /// # Permissions976        /// 977        /// * Collection Owner978        /// * Collection Admin979        /// * Current NFT owner980        /// 981        /// # Arguments982        /// 983        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).984        /// 985        /// * collection_id.986        /// 987        /// * item_id: ID of the item.988        #[weight = T::WeightInfo::approve()]989        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {990991            let sender = ensure_signed(origin)?;992993            // Transfer permissions check994            let target_collection = <Collection<T>>::get(collection_id);995            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||996                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),997                "Only item owner, collection owner and admins can approve");998999            if target_collection.access == AccessMode::WhiteList {1000                Self::check_white_list(collection_id, &sender)?;1001                Self::check_white_list(collection_id, &approved)?;1002            }10031004            // amount param stub1005            let amount = 100000000;10061007            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));1008            if list_exists {10091010                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));1011                let item_contains = list.iter().any(|i| i.approved == approved);10121013                if !item_contains {1014                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1015                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1016                }1017            } else {10181019                let mut list = Vec::new();1020                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1021                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1022            }10231024            Ok(())1025        }1026        1027        /// 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.1028        /// 1029        /// # Permissions1030        /// * Collection Owner1031        /// * Collection Admin1032        /// * Current NFT owner1033        /// * Address approved by current NFT owner1034        /// 1035        /// # Arguments1036        /// 1037        /// * from: Address that owns token.1038        /// 1039        /// * recipient: Address of token recipient.1040        /// 1041        /// * collection_id.1042        /// 1043        /// * item_id: ID of the item.1044        /// 1045        /// * value: Amount to transfer.1046        #[weight = T::WeightInfo::transfer_from()]1047        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10481049            let sender = ensure_signed(origin)?;1050            let mut appoved_transfer = false;10511052            // Check approve1053            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1054                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1055                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1056                if opt_item.is_some()1057                {1058                    appoved_transfer = true;1059                    ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1060                }1061            }10621063            // Transfer permissions check1064            let target_collection = <Collection<T>>::get(collection_id);1065            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1066                "Only item owner, collection owner and admins can modify items");10671068            if target_collection.access == AccessMode::WhiteList {1069                Self::check_white_list(collection_id, &sender)?;1070                Self::check_white_list(collection_id, &recipient)?;1071            }10721073            // remove approve1074            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1075                .into_iter().filter(|i| i.approved != sender.clone()).collect();1076            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);107710781079            match target_collection.mode1080            {1081                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1082                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1083                CollectionMode::ReFungible(_)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1084                _ => ()1085            };10861087            Ok(())1088        }10891090        ///1091        #[weight = 0]1092        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10931094            // let no_perm_mes = "You do not have permissions to modify this collection";1095            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1096            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1097            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10981099            // // on_nft_received  call11001101            // Self::transfer(origin, collection_id, item_id, new_owner)?;11021103            Ok(())1104        }11051106        /// Set off-chain data schema.1107        /// 1108        /// # Permissions1109        /// 1110        /// * Collection Owner1111        /// * Collection Admin1112        /// 1113        /// # Arguments1114        /// 1115        /// * collection_id.1116        /// 1117        /// * schema: String representing the offchain data schema.1118        #[weight = T::WeightInfo::set_variable_meta_data()]1119        pub fn set_variable_meta_data (1120            origin,1121            collection_id: u64,1122            item_id: u64,1123            data: Vec<u8>1124        ) -> DispatchResult {1125            let sender = ensure_signed(origin)?;1126            1127            Self::collection_exists(collection_id)?;11281129            // Modify permissions check1130            let target_collection = <Collection<T>>::get(collection_id);1131            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1132                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1133                "Only item owner, collection owner and admins can modify item");11341135            Self::item_exists(collection_id, item_id, &target_collection.mode)?;11361137            match target_collection.mode1138            {1139                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1140                CollectionMode::ReFungible(_)  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1141                _ => ()1142            };11431144            Ok(())1145        }1146        11471148        /// Set off-chain data schema.1149        /// 1150        /// # Permissions1151        /// 1152        /// * Collection Owner1153        /// * Collection Admin1154        /// 1155        /// # Arguments1156        /// 1157        /// * collection_id.1158        /// 1159        /// * schema: String representing the offchain data schema.1160        #[weight = T::WeightInfo::set_offchain_schema()]1161        pub fn set_offchain_schema(1162            origin,1163            collection_id: u64,1164            schema: Vec<u8>1165        ) -> DispatchResult {1166            let sender = ensure_signed(origin)?;1167            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11681169            let mut target_collection = <Collection<T>>::get(collection_id);1170            target_collection.offchain_schema = schema;1171            <Collection<T>>::insert(collection_id, target_collection);11721173            Ok(())1174        }11751176        /// Set const on-chain data schema.1177        /// 1178        /// # Permissions1179        /// 1180        /// * Collection Owner1181        /// * Collection Admin1182        /// 1183        /// # Arguments1184        /// 1185        /// * collection_id.1186        /// 1187        /// * schema: String representing the const on-chain data schema.1188        #[weight = T::WeightInfo::set_const_on_chain_schema()]1189        pub fn set_const_on_chain_schema (1190            origin,1191            collection_id: u64,1192            schema: Vec<u8>1193        ) -> DispatchResult {1194            let sender = ensure_signed(origin)?;1195            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11961197            let mut target_collection = <Collection<T>>::get(collection_id);1198            target_collection.const_on_chain_schema = schema;1199            <Collection<T>>::insert(collection_id, target_collection);12001201            Ok(())1202        }12031204        /// Set variable on-chain data schema.1205        /// 1206        /// # Permissions1207        /// 1208        /// * Collection Owner1209        /// * Collection Admin1210        /// 1211        /// # Arguments1212        /// 1213        /// * collection_id.1214        /// 1215        /// * schema: String representing the variable on-chain data schema.1216        #[weight = T::WeightInfo::set_const_on_chain_schema()]1217        pub fn set_variable_on_chain_schema (1218            origin,1219            collection_id: u64,1220            schema: Vec<u8>1221        ) -> DispatchResult {1222            let sender = ensure_signed(origin)?;1223            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;12241225            let mut target_collection = <Collection<T>>::get(collection_id);1226            target_collection.variable_on_chain_schema = schema;1227            <Collection<T>>::insert(collection_id, target_collection);12281229            Ok(())1230        }12311232        // Sudo permissions function1233        #[weight = 0]1234        pub fn set_chain_limits(1235            origin,1236            limits: ChainLimits1237        ) -> DispatchResult {1238            ensure_root(origin)?;1239            <ChainLimit>::put(limits);1240            Ok(())1241        }12421243        /// Enable smart contract self-sponsoring.1244        /// 1245        /// # Permissions1246        /// 1247        /// * Contract Owner1248        /// 1249        /// # Arguments1250        /// 1251        /// * contract address1252        /// * enable flag1253        /// 1254        #[weight = 0]1255        pub fn enable_contract_sponsoring(1256            origin,1257            contract_address: T::AccountId,1258            enable: bool1259        ) -> DispatchResult {1260            let sender = ensure_signed(origin)?;1261            let mut is_owner = false;1262            if <ContractOwner<T>>::contains_key(contract_address.clone()) {1263                let owner = <ContractOwner<T>>::get(&contract_address);1264                is_owner = sender == owner;1265            }1266            ensure!(is_owner, "Only contract owner may call this method");12671268            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1269            Ok(())1270        }12711272    }1273}12741275impl<T: Trait> Module<T> {12761277    fn can_create_items_in_collection(collection_id: u64, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {12781279        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1280            ensure!(collection.mint_mode == true, "Public minting is not allowed for this collection");1281            Self::check_white_list(collection_id, owner)?;1282            Self::check_white_list(collection_id, sender)?;1283        }12841285        Ok(())1286    }12871288    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1289        match target_collection.mode1290        {1291            CollectionMode::NFT => {1292                if let CreateItemData::NFT(data) = data {1293                    // check sizes1294                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1295                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1296                } else {1297                    fail!("Not NFT item data used to mint in NFT collection.");1298                }1299            },1300            CollectionMode::Fungible(_) => {1301                if let CreateItemData::Fungible(_) = data {1302                } else {1303                    fail!("Not Fungible item data used to mint in Fungible collection.");1304                }1305            },1306            CollectionMode::ReFungible(_) => {1307                if let CreateItemData::ReFungible(data) = data {13081309                    // check sizes1310                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, "const_data exceeded data limit.");1311                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, "variable_data exceeded data limit.");1312                } else {1313                    fail!("Not Re Fungible item data used to mint in Re Fungible collection.");1314                }1315            },1316            _ => { fail!("Unexpected collection type."); }1317        };13181319        Ok(())1320    }13211322    fn create_item_no_validation(collection_id: u64, collection: &CollectionType<T::AccountId>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1323        match data1324        {1325            CreateItemData::NFT(data) => {1326                let item = NftItemType {1327                    collection: collection_id,1328                    owner,1329                    const_data: data.const_data,1330                    variable_data: data.variable_data1331                };13321333                Self::add_nft_item(item)?;1334            },1335            CreateItemData::Fungible(_) => {1336                let item = FungibleItemType {1337                    collection: collection_id,1338                    owner,1339                    value: (10 as u128).pow(collection.decimal_points)1340                };13411342                Self::add_fungible_item(item)?;1343            },1344            CreateItemData::ReFungible(data) => {1345                let mut owner_list = Vec::new();1346                let value = (10 as u128).pow(collection.decimal_points);1347                owner_list.push(Ownership {owner: owner.clone(), fraction: value});13481349                let item = ReFungibleItemType {1350                    collection: collection_id,1351                    owner: owner_list,1352                    const_data: data.const_data,1353                    variable_data: data.variable_data1354                };13551356                Self::add_refungible_item(item)?;1357            }1358        };135913601361        // call event1362        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));13631364        Ok(())1365    }13661367    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1368        let current_index = <ItemListIndex>::get(item.collection)1369            .checked_add(1)1370            .expect("Item list index id error");1371        let itemcopy = item.clone();1372        let owner = item.owner.clone();1373        let value = item.value as u64;13741375        Self::add_token_index(item.collection, current_index, owner.clone())?;13761377        <ItemListIndex>::insert(item.collection, current_index);1378        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);13791380        // Add current block1381        let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1382        <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1383        1384        // Update balance1385        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1386            .checked_add(value)1387            .unwrap();1388        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);13891390        Ok(())1391    }13921393    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1394        let current_index = <ItemListIndex>::get(item.collection)1395            .checked_add(1)1396            .expect("Item list index id error");1397        let itemcopy = item.clone();13981399        let value = item.owner.first().unwrap().fraction as u64;1400        let owner = item.owner.first().unwrap().owner.clone();14011402        Self::add_token_index(item.collection, current_index, owner.clone())?;14031404        <ItemListIndex>::insert(item.collection, current_index);1405        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);14061407        // Add current block1408        let block_number: T::BlockNumber = 0.into();1409        <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);14101411        // Update balance1412        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1413            .checked_add(value)1414            .unwrap();1415        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);14161417        Ok(())1418    }14191420    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1421        let current_index = <ItemListIndex>::get(item.collection)1422            .checked_add(1)1423            .expect("Item list index id error");14241425        let item_owner = item.owner.clone();1426        let collection_id = item.collection.clone();1427        Self::add_token_index(collection_id, current_index, item.owner.clone())?;14281429        <ItemListIndex>::insert(collection_id, current_index);1430        <NftItemList<T>>::insert(collection_id, current_index, item);14311432        // Add current block1433        let block_number: T::BlockNumber = 0.into();1434        <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);14351436        // Update balance1437        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1438            .checked_add(1)1439            .unwrap();1440        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);14411442        Ok(())1443    }14441445    fn burn_refungible_item(1446        collection_id: u64,1447        item_id: u64,1448        owner: T::AccountId,1449    ) -> DispatchResult {1450        ensure!(1451            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1452            "Item does not exists"1453        );1454        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1455        let item = collection1456            .owner1457            .iter()1458            .filter(|&i| i.owner == owner)1459            .next()1460            .unwrap();1461        Self::remove_token_index(collection_id, item_id, owner.clone())?;14621463        // remove approve list1464        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));14651466        // update balance1467        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1468            .checked_sub(item.fraction as u64)1469            .unwrap();1470        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);14711472        <ReFungibleItemList<T>>::remove(collection_id, item_id);14731474        Ok(())1475    }14761477    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1478        ensure!(1479            <NftItemList<T>>::contains_key(collection_id, item_id),1480            "Item does not exists"1481        );1482        let item = <NftItemList<T>>::get(collection_id, item_id);1483        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;14841485        // remove approve list1486        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));14871488        // update balance1489        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1490            .checked_sub(1)1491            .unwrap();1492        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1493        <NftItemList<T>>::remove(collection_id, item_id);14941495        Ok(())1496    }14971498    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1499        ensure!(1500            <FungibleItemList<T>>::contains_key(collection_id, item_id),1501            "Item does not exists"1502        );1503        let item = <FungibleItemList<T>>::get(collection_id, item_id);1504        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;15051506        // remove approve list1507        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));15081509        // update balance1510        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1511            .checked_sub(item.value as u64)1512            .unwrap();1513        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);15141515        <FungibleItemList<T>>::remove(collection_id, item_id);15161517        Ok(())1518    }15191520    fn collection_exists(collection_id: u64) -> DispatchResult {1521        ensure!(1522            <Collection<T>>::contains_key(collection_id),1523            "This collection does not exist"1524        );1525        Ok(())1526    }15271528    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1529        Self::collection_exists(collection_id)?;15301531        let target_collection = <Collection<T>>::get(collection_id);1532        ensure!(1533            subject == target_collection.owner,1534            "You do not own this collection"1535        );15361537        Ok(())1538    }15391540    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1541        let target_collection = <Collection<T>>::get(collection_id);1542        let mut result: bool = subject == target_collection.owner;1543        let exists = <AdminList<T>>::contains_key(collection_id);15441545        if !result & exists {1546            if <AdminList<T>>::get(collection_id).contains(&subject) {1547                result = true1548            }1549        }15501551        result1552    }15531554    fn check_owner_or_admin_permissions(1555        collection_id: u64,1556        subject: T::AccountId,1557    ) -> DispatchResult {1558        Self::collection_exists(collection_id)?;1559        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());15601561        ensure!(1562            result,1563            "You do not have permissions to modify this collection"1564        );1565        Ok(())1566    }15671568    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1569        let target_collection = <Collection<T>>::get(collection_id);15701571        match target_collection.mode {1572            CollectionMode::NFT => {1573                <NftItemList<T>>::get(collection_id, item_id).owner == subject1574            }1575            CollectionMode::Fungible(_) => {1576                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1577            }1578            CollectionMode::ReFungible(_) => {1579                <ReFungibleItemList<T>>::get(collection_id, item_id)1580                    .owner1581                    .iter()1582                    .any(|i| i.owner == subject)1583            }1584            CollectionMode::Invalid => false,1585        }1586    }15871588    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1589        let mes = "Address is not in white list";1590        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1591        let wl = <WhiteList<T>>::get(collection_id);1592        ensure!(wl.contains(address), mes);15931594        Ok(())1595    }15961597    fn transfer_fungible(1598        collection_id: u64,1599        item_id: u64,1600        value: u64,1601        owner: T::AccountId,1602        new_owner: T::AccountId,1603    ) -> DispatchResult {1604        ensure!(1605            <FungibleItemList<T>>::contains_key(collection_id, item_id),1606            "Item not exists"1607        );16081609        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1610        let amount = full_item.value;16111612        ensure!(amount >= value.into(), "Item balance not enouth");16131614        // update balance1615        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1616            .checked_sub(value)1617            .unwrap();1618        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);16191620        let mut new_owner_account_id = 0;1621        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1622        if new_owner_items.len() > 0 {1623            new_owner_account_id = new_owner_items[0];1624        }16251626        let val64 = value.into();16271628        // transfer1629        if amount == val64 && new_owner_account_id == 0 {1630            // change owner1631            // new owner do not have account1632            let mut new_full_item = full_item.clone();1633            new_full_item.owner = new_owner.clone();1634            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);16351636            // update balance1637            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1638                .checked_add(value)1639                .unwrap();1640            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16411642            // update index collection1643            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1644        } else {1645            let mut new_full_item = full_item.clone();1646            new_full_item.value -= val64;16471648            // separate amount1649            if new_owner_account_id > 0 {1650                // new owner has account1651                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1652                item.value += val64;16531654                // update balance1655                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1656                    .checked_add(value)1657                    .unwrap();1658                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);16591660                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1661            } else {1662                // new owner do not have account1663                let item = FungibleItemType {1664                    collection: collection_id,1665                    owner: new_owner.clone(),1666                    value: val64,1667                };16681669                Self::add_fungible_item(item)?;1670            }16711672            if amount == val64 {1673                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;16741675                // remove approve list1676                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1677                <FungibleItemList<T>>::remove(collection_id, item_id);1678            }16791680            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1681        }16821683        Ok(())1684    }16851686    fn transfer_refungible(1687        collection_id: u64,1688        item_id: u64,1689        value: u64,1690        owner: T::AccountId,1691        new_owner: T::AccountId,1692    ) -> DispatchResult {1693        ensure!(1694            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1695            "Item not exists"1696        );16971698        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1699        let item = full_item1700            .owner1701            .iter()1702            .filter(|i| i.owner == owner)1703            .next()1704            .unwrap();1705        let amount = item.fraction;17061707        ensure!(amount >= value.into(), "Item balance not enouth");17081709        // update balance1710        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1711            .checked_sub(value)1712            .unwrap();1713        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17141715        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1716            .checked_add(value)1717            .unwrap();1718        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);17191720        let old_owner = item.owner.clone();1721        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1722        let val64 = value.into();17231724        // transfer1725        if amount == val64 && !new_owner_has_account {1726            // change owner1727            // new owner do not have account1728            let mut new_full_item = full_item.clone();1729            new_full_item1730                .owner1731                .iter_mut()1732                .find(|i| i.owner == owner)1733                .unwrap()1734                .owner = new_owner.clone();1735            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);17361737            // update index collection1738            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1739        } else {1740            let mut new_full_item = full_item.clone();1741            new_full_item1742                .owner1743                .iter_mut()1744                .find(|i| i.owner == owner)1745                .unwrap()1746                .fraction -= val64;17471748            // separate amount1749            if new_owner_has_account {1750                // new owner has account1751                new_full_item1752                    .owner1753                    .iter_mut()1754                    .find(|i| i.owner == new_owner)1755                    .unwrap()1756                    .fraction += val64;1757            } else {1758                // new owner do not have account1759                new_full_item.owner.push(Ownership {1760                    owner: new_owner.clone(),1761                    fraction: val64,1762                });1763                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1764            }17651766            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1767        }17681769        Ok(())1770    }17711772    fn transfer_nft(1773        collection_id: u64,1774        item_id: u64,1775        sender: T::AccountId,1776        new_owner: T::AccountId,1777    ) -> DispatchResult {1778        ensure!(1779            <NftItemList<T>>::contains_key(collection_id, item_id),1780            "Item not exists"1781        );17821783        let mut item = <NftItemList<T>>::get(collection_id, item_id);17841785        ensure!(1786            sender == item.owner,1787            "sender parameter and item owner must be equal"1788        );17891790        // update balance1791        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1792            .checked_sub(1)1793            .unwrap();1794        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);17951796        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1797            .checked_add(1)1798            .unwrap();1799        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);18001801        // change owner1802        let old_owner = item.owner.clone();1803        item.owner = new_owner.clone();1804        <NftItemList<T>>::insert(collection_id, item_id, item);18051806        // update index collection1807        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;18081809        // reset approved list1810        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1811        Ok(())1812    }1813    1814    fn item_exists(1815        collection_id: u64,1816        item_id: u64,1817        mode: &CollectionMode1818    ) -> DispatchResult {1819        match mode {1820            CollectionMode::NFT => ensure!(<NftItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1821            CollectionMode::ReFungible(_) => ensure!(<ReFungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1822            CollectionMode::Fungible(_) => ensure!(<FungibleItemList<T>>::contains_key(collection_id, item_id), "Item does not exists"),1823            _ => ()1824        };1825        1826        Ok(())1827    }18281829    fn set_re_fungible_variable_data(1830        collection_id: u64,1831        item_id: u64,1832        data: Vec<u8>1833    ) -> DispatchResult {1834        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);18351836        item.variable_data = data;18371838        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);18391840        Ok(())1841    }18421843    fn set_nft_variable_data(1844        collection_id: u64,1845        item_id: u64,1846        data: Vec<u8>1847    ) -> DispatchResult {1848        let mut item = <NftItemList<T>>::get(collection_id, item_id);1849        1850        item.variable_data = data;18511852        <NftItemList<T>>::insert(collection_id, item_id, item);1853        1854        Ok(())1855    }18561857    fn init_collection(item: &CollectionType<T::AccountId>) {1858        // check params1859        assert!(1860            item.decimal_points <= 4,1861            "decimal_points parameter must be lower than 4"1862        );1863        assert!(1864            item.name.len() <= 64,1865            "Collection name can not be longer than 63 char"1866        );1867        assert!(1868            item.name.len() <= 256,1869            "Collection description can not be longer than 255 char"1870        );1871        assert!(1872            item.token_prefix.len() <= 16,1873            "Token prefix can not be longer than 15 char"1874        );18751876        // Generate next collection ID1877        let next_id = CreatedCollectionCount::get()1878            .checked_add(1)1879            .expect("collection id error");18801881        CreatedCollectionCount::put(next_id);1882    }18831884    fn init_nft_token(item: &NftItemType<T::AccountId>) {1885        let current_index = <ItemListIndex>::get(item.collection)1886            .checked_add(1)1887            .expect("Item list index id error");18881889        let item_owner = item.owner.clone();1890        let collection_id = item.collection.clone();1891        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();18921893        <ItemListIndex>::insert(collection_id, current_index);18941895        // Update balance1896        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1897            .checked_add(1)1898            .unwrap();1899        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1900    }19011902    fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1903        let current_index = <ItemListIndex>::get(item.collection)1904            .checked_add(1)1905            .expect("Item list index id error");1906        let owner = item.owner.clone();1907        let value = item.value as u64;19081909        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19101911        <ItemListIndex>::insert(item.collection, current_index);19121913        // Update balance1914        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1915            .checked_add(value)1916            .unwrap();1917        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1918    }19191920    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1921        let current_index = <ItemListIndex>::get(item.collection)1922            .checked_add(1)1923            .expect("Item list index id error");19241925        let value = item.owner.first().unwrap().fraction as u64;1926        let owner = item.owner.first().unwrap().owner.clone();19271928        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();19291930        <ItemListIndex>::insert(item.collection, current_index);19311932        // Update balance1933        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1934            .checked_add(value)1935            .unwrap();1936        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1937    }19381939    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {19401941        // add to account limit1942        if <AccountItemCount<T>>::contains_key(owner.clone()) {19431944            // bound Owned tokens by a single address1945            let count = <AccountItemCount<T>>::get(owner.clone());1946            ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");19471948            <AccountItemCount<T>>::insert(owner.clone(), 1949                count.checked_add(1).unwrap());1950        }1951        else {1952            <AccountItemCount<T>>::insert(owner.clone(), 1);1953        }19541955        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1956        if list_exists {1957            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1958            let item_contains = list.contains(&item_index.clone());19591960            if !item_contains {1961                list.push(item_index.clone());1962            }19631964            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1965        } else {1966            let mut itm = Vec::new();1967            itm.push(item_index.clone());1968            <AddressTokens<T>>::insert(collection_id, owner, itm);1969            1970        }19711972        Ok(())1973    }19741975    fn remove_token_index(1976        collection_id: u64,1977        item_index: u64,1978        owner: T::AccountId,1979    ) -> DispatchResult {19801981        // update counter1982        <AccountItemCount<T>>::insert(owner.clone(), 1983            <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());198419851986        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1987        if list_exists {1988            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1989            let item_contains = list.contains(&item_index.clone());19901991            if item_contains {1992                list.retain(|&item| item != item_index);1993                <AddressTokens<T>>::insert(collection_id, owner, list);1994            }1995        }19961997        Ok(())1998    }19992000    fn move_token_index(2001        collection_id: u64,2002        item_index: u64,2003        old_owner: T::AccountId,2004        new_owner: T::AccountId,2005    ) -> DispatchResult {2006        Self::remove_token_index(collection_id, item_index, old_owner)?;2007        Self::add_token_index(collection_id, item_index, new_owner)?;20082009        Ok(())2010    }2011}20122013////////////////////////////////////////////////////////////////////////////////////////////////////2014// Economic models2015// #region20162017/// Fee multiplier.2018pub type Multiplier = FixedU128;20192020type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2021    <T as system::Trait>::AccountId,2022>>::Balance;2023type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<2024    <T as system::Trait>::AccountId,2025>>::NegativeImbalance;20262027/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2028/// in the queue.2029#[derive(Encode, Decode, Clone, Eq, PartialEq)]2030pub struct ChargeTransactionPayment<T: Trait + Send + Sync>(2031    #[codec(compact)] BalanceOf<T>2032);20332034impl<T: Trait + Send + Sync> sp_std::fmt::Debug2035    for ChargeTransactionPayment<T>2036{2037    #[cfg(feature = "std")]2038    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2039        write!(f, "ChargeTransactionPayment<{:?}>", self.0)2040    }2041    #[cfg(not(feature = "std"))]2042    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2043        Ok(())2044    }2045}20462047impl<T: Trait + Send + Sync> ChargeTransactionPayment<T>2048where2049    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2050    BalanceOf<T>: Send + Sync + FixedPointOperand,2051{2052    /// utility constructor. Used only in client/factory code.2053    pub fn from(fee: BalanceOf<T>) -> Self {2054        Self(fee)2055    }20562057    pub fn traditional_fee(2058        len: usize,2059        info: &DispatchInfoOf<T::Call>,2060        tip: BalanceOf<T>,2061    ) -> BalanceOf<T>2062    where2063        T::Call: Dispatchable<Info = DispatchInfo>,2064    {2065        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2066    }20672068    fn withdraw_fee(2069        &self,2070        who: &T::AccountId,2071        call: &T::Call,2072        info: &DispatchInfoOf<T::Call>,2073        len: usize,2074    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {2075        let tip = self.0;20762077        // Set fee based on call type. Creating collection costs 1 Unique.2078        // All other transactions have traditional fees so far2079        // let fee = match call.is_sub_type() {2080        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2081        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2082        //                                                 // _ => <BalanceOf<T>>::from(100)2083        // };2084        let fee = Self::traditional_fee(len, info, tip);20852086        // Determine who is paying transaction fee based on ecnomic model2087        // Parse call to extract collection ID and access collection sponsor2088        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2089            Some(Call::create_item(collection_id, _properties, _owner)) => {2090                <Collection<T>>::get(collection_id).sponsor2091            }2092            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {2093                let _collection_mode = <Collection<T>>::get(collection_id).mode;20942095                // sponsor timeout2096                let sponsor_transfer = match _collection_mode {2097                    CollectionMode::NFT => {2098                        let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);2099                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2100                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2101                        if block_number >= limit_time {2102                            <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);2103                            true2104                        }2105                        else {2106                            false2107                        }2108                    }2109                    CollectionMode::Fungible(_) => {2110                        let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);2111                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2112                        if basket.iter().any(|i| i.address == _new_owner.clone())2113                        {2114                            let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();2115                            let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();2116                            if block_number >= limit_time {2117                                basket.retain(|x| x.address == item.address);2118                                basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });2119                                <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);2120                                true2121                            }2122                            else {2123                                false2124                            }2125                        }2126                        else {2127                            basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});2128                            true2129                        }2130                    }2131                    CollectionMode::ReFungible(_) => {2132                        let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);2133                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2134                        let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();2135                        if block_number >= limit_time {2136                            <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);2137                            true2138                        } else {2139                            false2140                        }2141                    }2142                    _ => {2143                        false2144                    },2145                };21462147                if !sponsor_transfer {2148                    T::AccountId::default()2149                } else {2150                    <Collection<T>>::get(collection_id).sponsor2151                }2152            }21532154            _ => T::AccountId::default(),2155        };21562157        // Sponsor smart contracts2158        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {21592160            // On instantiation: set the contract owner2161            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, data)) => {21622163                let new_contract_address = <T as pallet_contracts::Trait>::DetermineContractAddress::contract_address_for(2164                    code_hash,2165                    &data,2166                    &who,2167                );2168                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());21692170                T::AccountId::default()2171            },21722173            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2174            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {21752176                let mut sp = T::AccountId::default();2177                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());2178                if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2179                    if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2180                        sp = called_contract;2181                    }2182                }21832184                sp2185            },21862187            _ => sponsor,2188        };21892190        let mut who_pays_fee: T::AccountId = sponsor.clone();2191        if sponsor == T::AccountId::default() {2192            who_pays_fee = who.clone();2193        }21942195        // Only mess with balances if fee is not zero.2196        if fee.is_zero() {2197            return Ok((fee, None));2198        }21992200        match <T as transaction_payment::Trait>::Currency::withdraw(2201            &who_pays_fee,2202            fee,2203            if tip.is_zero() {2204                WithdrawReason::TransactionPayment.into()2205            } else {2206                WithdrawReason::TransactionPayment | WithdrawReason::Tip2207            },2208            ExistenceRequirement::KeepAlive,2209        ) {2210            Ok(imbalance) => Ok((fee, Some(imbalance))),2211            Err(_) => Err(InvalidTransaction::Payment.into()),2212        }2213    }2214}221522162217impl<T: Trait + Send + Sync> SignedExtension2218    for ChargeTransactionPayment<T>2219where2220    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2221    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2222{2223    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2224    type AccountId = T::AccountId;2225    type Call = T::Call;2226    type AdditionalSigned = ();2227    type Pre = (2228        BalanceOf<T>,2229        Self::AccountId,2230        Option<NegativeImbalanceOf<T>>,2231        BalanceOf<T>,2232    );2233    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2234        Ok(())2235    }22362237    fn validate(2238        &self,2239        _who: &Self::AccountId,2240        _call: &Self::Call,2241        _info: &DispatchInfoOf<Self::Call>,2242        _len: usize,2243    ) -> TransactionValidity {2244        Ok(ValidTransaction::default())2245    }22462247    fn pre_dispatch(2248        self,2249        who: &Self::AccountId,2250        call: &Self::Call,2251        info: &DispatchInfoOf<Self::Call>,2252        len: usize,2253    ) -> Result<Self::Pre, TransactionValidityError> {2254        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2255        Ok((self.0, who.clone(), imbalance, fee))2256    }22572258    fn post_dispatch(2259        pre: Self::Pre,2260        info: &DispatchInfoOf<Self::Call>,2261        post_info: &PostDispatchInfoOf<Self::Call>,2262        len: usize,2263        _result: &DispatchResult,2264    ) -> Result<(), TransactionValidityError> {2265        let (tip, who, imbalance, fee) = pre;2266        if let Some(payed) = imbalance {2267            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(2268                len as u32, info, post_info, tip,2269            );2270            let refund = fee.saturating_sub(actual_fee);2271            let actual_payment =2272                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(2273                    &who, refund,2274                ) {2275                    Ok(refund_imbalance) => {2276                        // The refund cannot be larger than the up front payed max weight.2277                        // `PostDispatchInfo::calc_unspent` guards against such a case.2278                        match payed.offset(refund_imbalance) {2279                            Ok(actual_payment) => actual_payment,2280                            Err(_) => return Err(InvalidTransaction::Payment.into()),2281                        }2282                    }2283                    // We do not recreate the account using the refund. The up front payment2284                    // is gone in that case.2285                    Err(_) => payed,2286                };2287            let imbalances = actual_payment.split(tip);2288            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(2289                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),2290            );2291        }2292        Ok(())2293    }2294}22952296// #endregion