git.delta.rocks / unique-network / refs/commits / 8556bf031c94

difftreelog

NFTPAR-107: RustDocs for All Externally Callable Methods.

sotmorskiy2020-10-07parent: #ab1a681.patch.diff
in: master

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 serde::*;56use codec::{Decode, Encode};7pub use frame_support::{8    construct_runtime, decl_event, decl_module, decl_storage,9    dispatch::DispatchResult,10    ensure, parameter_types,11    traits::{12        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,13        Randomness, WithdrawReason,14    },15    weights::{16        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},17        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,18        WeightToFeePolynomial,19    },20    IsSubType, StorageValue,21};2223use frame_system::{self as system, ensure_signed};24use sp_runtime::sp_std::prelude::Vec;25use sp_runtime::{26    traits::{27        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,28        SignedExtension, Zero,29    },30    transaction_validity::{31        InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,32        ValidTransaction,33    },34    FixedPointOperand, FixedU128,35};3637#[cfg(test)]38mod mock;3940#[cfg(test)]41mod tests;4243// Structs44// #region4546#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]47#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]48pub enum CollectionMode {49    Invalid,50    // custom data size51    NFT(u32),52    // decimal points53    Fungible(u32),54    // custom data size and decimal points55    ReFungible(u32, u32),56}5758impl Into<u8> for CollectionMode {59    fn into(self) -> u8 {60        match self {61            CollectionMode::Invalid => 0,62            CollectionMode::NFT(_) => 1,63            CollectionMode::Fungible(_) => 2,64            CollectionMode::ReFungible(_, _) => 3,65        }66    }67}6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum AccessMode {72    Normal,73    WhiteList,74}75impl Default for AccessMode {76    fn default() -> Self {77        Self::Normal78    }79}8081impl Default for CollectionMode {82    fn default() -> Self {83        Self::Invalid84    }85}8687#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]88#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]89pub struct Ownership<AccountId> {90    pub owner: AccountId,91    pub fraction: u128,92}9394#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub struct CollectionType<AccountId> {97    pub owner: AccountId,98    pub mode: CollectionMode,99    pub access: AccessMode,100    pub decimal_points: u32,101    pub name: Vec<u16>,        // 64 include null escape char102    pub description: Vec<u16>, // 256 include null escape char103    pub token_prefix: Vec<u8>, // 16 include null escape char104    pub custom_data_size: u32,105    pub mint_mode: bool,106    pub offchain_schema: Vec<u8>,107    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender108    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship109}110111#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub struct CollectionAdminsType<AccountId> {114    pub admin: AccountId,115    pub collection_id: u64,116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct NftItemType<AccountId> {121    pub collection: u64,122    pub owner: AccountId,123    pub data: Vec<u8>,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct FungibleItemType<AccountId> {129    pub collection: u64,130    pub owner: AccountId,131    pub value: u128,132}133134#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub struct ReFungibleItemType<AccountId> {137    pub collection: u64,138    pub owner: Vec<Ownership<AccountId>>,139    pub 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}159160pub trait Trait: system::Trait {161    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;162}163164// #endregion165166decl_storage! {167    trait Store for Module<T: Trait> as Nft {168169        // Private members170        NextCollectionID: u64;171        CreatedCollectionCount: u64;172        ChainVersion: u64;173        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;174175        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;176        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;177        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;178179        /// Balance owner per collection map180        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;181182        /// second parameter: item id + owner account id183        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;184185        /// Item collections186        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;187        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;188        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;189190        /// Index list191        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;192193        // Sponsorship194        pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;195        pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;196    }197    add_extra_genesis {198        build(|config: &GenesisConfig<T>| {199			// Modification of storage200            for (_num, _c) in &config.collection {201                <Module<T>>::init_collection(_c);202            }203204            for (_num, _q, _i) in &config.nft_item_id {205                <Module<T>>::init_nft_token(_i);206            }207208            for (_num, _q, _i) in &config.fungible_item_id {209                <Module<T>>::init_fungible_token(_i);210            }211212            for (_num, _q, _i) in &config.refungible_item_id {213                <Module<T>>::init_refungible_token(_i);214            }215		})216    }217}218219decl_event!(220    pub enum Event<T>221    where222        AccountId = <T as system::Trait>::AccountId,223    {224        Created(u64, u8, AccountId),225        ItemCreated(u64, u64),226        ItemDestroyed(u64, u64),227    }228);229230decl_module! {231    pub struct Module<T: Trait> for enum Call where origin: T::Origin {232233        fn deposit_event() = default;234235        fn on_initialize(now: T::BlockNumber) -> Weight {236237            if ChainVersion::get() < 2238            {239                let value = NextCollectionID::get();240                CreatedCollectionCount::put(value);241                ChainVersion::put(2);242            }243244            0245        }246247        // Create collection of NFT with given parameters248        //249        // @param customDataSz size of custom data in each collection item250        // returns collection ID251        #[weight = 0]252        pub fn create_collection(origin,253                                 collection_name: Vec<u16>,254                                 collection_description: Vec<u16>,255                                 token_prefix: Vec<u8>,256                                 mode: CollectionMode) -> DispatchResult {257258            // Anyone can create a collection259            let who = ensure_signed(origin)?;260            let custom_data_size = match mode {261                CollectionMode::NFT(size) => size,262                CollectionMode::ReFungible(size, _) => size,263                _ => 0264            };265266            let decimal_points = match mode {267                CollectionMode::Fungible(points) => points,268                CollectionMode::ReFungible(_, points) => points,269                _ => 0270            };271272            // check params273            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");274275            let mut name = collection_name.to_vec();276            name.push(0);277            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");278279            let mut description = collection_description.to_vec();280            description.push(0);281            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");282283            let mut prefix = token_prefix.to_vec();284            prefix.push(0);285            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");286287            // Generate next collection ID288            let next_id = CreatedCollectionCount::get()289                .checked_add(1)290                .expect("collection id error");291292            CreatedCollectionCount::put(next_id);293294            // Create new collection295            let new_collection = CollectionType {296                owner: who.clone(),297                name: name,298                mode: mode.clone(),299                mint_mode: false,300                access: AccessMode::Normal,301                description: description,302                decimal_points: decimal_points,303                token_prefix: prefix,304                offchain_schema: Vec::new(),305                custom_data_size: custom_data_size,306                sponsor: T::AccountId::default(),307                unconfirmed_sponsor: T::AccountId::default(),308            };309310            // Add new collection to map311            <Collection<T>>::insert(next_id, new_collection);312313            // call event314            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));315316            Ok(())317        }318319        #[weight = 0]320        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {321322            let sender = ensure_signed(origin)?;323            Self::check_owner_permissions(collection_id, sender)?;324325            // TODO Items remove326            <AddressTokens<T>>::remove_prefix(collection_id);327            <ApprovedList<T>>::remove_prefix(collection_id);328            <Balance<T>>::remove_prefix(collection_id);329            <ItemListIndex>::remove(collection_id);330            <AdminList<T>>::remove(collection_id);331            <Collection<T>>::remove(collection_id);332            <WhiteList<T>>::remove(collection_id);333334            Ok(())335        }336337        #[weight = 0]338        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{339340            let sender = ensure_signed(origin)?;341            Self::check_owner_or_admin_permissions(collection_id, sender)?;342343            let mut white_list_collection: Vec<T::AccountId>;344            if <WhiteList<T>>::contains_key(collection_id) {345                white_list_collection = <WhiteList<T>>::get(collection_id);346                if !white_list_collection.contains(&address.clone())347                {348                    white_list_collection.push(address.clone());349                }350            }351            else {352                white_list_collection = Vec::new();353                white_list_collection.push(address.clone());354            }355356            <WhiteList<T>>::insert(collection_id, white_list_collection);357            Ok(())358        }359360        #[weight = 0]361        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{362363            let sender = ensure_signed(origin)?;364            Self::check_owner_or_admin_permissions(collection_id, sender)?;365366            if <WhiteList<T>>::contains_key(collection_id) {367                let mut white_list_collection = <WhiteList<T>>::get(collection_id);368                if white_list_collection.contains(&address.clone())369                {370                    white_list_collection.retain(|i| *i != address.clone());371                    <WhiteList<T>>::insert(collection_id, white_list_collection);372                }373            }374375            Ok(())376        }377378        #[weight = 0]379        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult380        {381            let sender = ensure_signed(origin)?;382383            Self::check_owner_permissions(collection_id, sender)?;384            let mut target_collection = <Collection<T>>::get(collection_id);385            target_collection.access = mode;386            <Collection<T>>::insert(collection_id, target_collection);387388            Ok(())389        }390391        #[weight = 0]392        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult393        {394            let sender = ensure_signed(origin)?;395396            Self::check_owner_permissions(collection_id, sender)?;397            let mut target_collection = <Collection<T>>::get(collection_id);398            target_collection.mint_mode = mint_permission;399            <Collection<T>>::insert(collection_id, target_collection);400401            Ok(())402        }403404        #[weight = 0]405        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {406407            let sender = ensure_signed(origin)?;408            Self::check_owner_permissions(collection_id, sender)?;409            let mut target_collection = <Collection<T>>::get(collection_id);410            target_collection.owner = new_owner;411            <Collection<T>>::insert(collection_id, target_collection);412413            Ok(())414        }415416        #[weight = 0]417        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {418419            let sender = ensure_signed(origin)?;420            Self::check_owner_or_admin_permissions(collection_id, sender)?;421            let mut admin_arr: Vec<T::AccountId> = Vec::new();422423            if <AdminList<T>>::contains_key(collection_id)424            {425                admin_arr = <AdminList<T>>::get(collection_id);426                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");427            }428429            admin_arr.push(new_admin_id);430            <AdminList<T>>::insert(collection_id, admin_arr);431432            Ok(())433        }434435        #[weight = 0]436        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {437438            let sender = ensure_signed(origin)?;439            Self::check_owner_or_admin_permissions(collection_id, sender)?;440441            if <AdminList<T>>::contains_key(collection_id)442            {443                let mut admin_arr = <AdminList<T>>::get(collection_id);444                admin_arr.retain(|i| *i != account_id);445                <AdminList<T>>::insert(collection_id, admin_arr);446            }447448            Ok(())449        }450451        #[weight = 0]452        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {453454            let sender = ensure_signed(origin)?;455            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");456457            let mut target_collection = <Collection<T>>::get(collection_id);458            ensure!(sender == target_collection.owner, "You do not own this collection");459460            target_collection.unconfirmed_sponsor = new_sponsor;461            <Collection<T>>::insert(collection_id, target_collection);462463            Ok(())464        }465466        #[weight = 0]467        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {468469            let sender = ensure_signed(origin)?;470            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");471472            let mut target_collection = <Collection<T>>::get(collection_id);473            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");474475            target_collection.sponsor = target_collection.unconfirmed_sponsor;476            target_collection.unconfirmed_sponsor = T::AccountId::default();477            <Collection<T>>::insert(collection_id, target_collection);478479            Ok(())480        }481482        #[weight = 0]483        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {484485            let sender = ensure_signed(origin)?;486            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");487488            let mut target_collection = <Collection<T>>::get(collection_id);489            ensure!(sender == target_collection.owner, "You do not own this collection");490491            target_collection.sponsor = T::AccountId::default();492            <Collection<T>>::insert(collection_id, target_collection);493494            Ok(())495        }496497        #[weight = 0]498        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {499500            let sender = ensure_signed(origin)?;501            Self::collection_exists(collection_id)?;502            let target_collection = <Collection<T>>::get(collection_id);503504            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {505                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");506                Self::check_white_list(collection_id, &owner)?;507                Self::check_white_list(collection_id, &sender)?;508            }509510            match target_collection.mode511            {512                CollectionMode::NFT(_) => {513514                    // check size515                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");516517                    // Create nft item518                    let item = NftItemType {519                        collection: collection_id,520                        owner: owner,521                        data: properties.clone(),522                    };523524                    Self::add_nft_item(item)?;525526                },527                CollectionMode::Fungible(_) => {528529                    // check size530                    ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");531532                    let item = FungibleItemType {533                        collection: collection_id,534                        owner: owner,535                        value: (10 as u128).pow(target_collection.decimal_points)536                    };537538                    Self::add_fungible_item(item)?;539                },540                CollectionMode::ReFungible(_, _) => {541542                    // check size543                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");544545                    let mut owner_list = Vec::new();546                    let value = (10 as u128).pow(target_collection.decimal_points);547                    owner_list.push(Ownership {owner: owner.clone(), fraction: value});548549                    let item = ReFungibleItemType {550                        collection: collection_id,551                        owner: owner_list,552                        data: properties.clone()553                    };554555                    Self::add_refungible_item(item)?;556                },557                _ => { ensure!(1 == 0,"just error"); }558559            };560561            // call event562            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));563564            Ok(())565        }566567        #[weight = 0]568        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {569570            let sender = ensure_signed(origin)?;571            Self::collection_exists(collection_id)?;572573            // Transfer permissions check574            let target_collection = <Collection<T>>::get(collection_id);575            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 576                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 577                "Only item owner, collection owner and admins can modify item");578579            if target_collection.access == AccessMode::WhiteList {580                Self::check_white_list(collection_id, &sender)?;581            }582583            match target_collection.mode584            {585                CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,586                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,587                CollectionMode::ReFungible(_, _)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,588                _ => ()589            };590591            // call event592            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));593594            Ok(())595        }596597        #[weight = 0]598        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {599600            let sender = ensure_signed(origin)?;601602            // Transfer permissions check603            let target_collection = <Collection<T>>::get(collection_id);604            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 605                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 606                "Only item owner, collection owner and admins can modify item");607608            if target_collection.access == AccessMode::WhiteList {609                Self::check_white_list(collection_id, &sender)?;610                Self::check_white_list(collection_id, &recipient)?;611            }612613            match target_collection.mode614            {615                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,616                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,617                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,618                _ => ()619            };620621            Ok(())622        }623624        #[weight = 0]625        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {626627            let sender = ensure_signed(origin)?;628629            // Transfer permissions check630            let target_collection = <Collection<T>>::get(collection_id);631            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 632                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 633                "Only item owner, collection owner and admins can approve");634635            if target_collection.access == AccessMode::WhiteList {636                Self::check_white_list(collection_id, &sender)?;637                Self::check_white_list(collection_id, &approved)?;638            }639640            // amount param stub641            let amount = 100000000;642643            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));644            if list_exists {645646                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));647                let item_contains = list.iter().any(|i| i.approved == approved);648649                if !item_contains {650                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });651                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);652                }653            } else {654655                let mut list = Vec::new();656                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });657                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);658            }659660            Ok(())661        }662663        #[weight = 0]664        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {665666            let sender = ensure_signed(origin)?;667            let mut appoved_transfer = false;668669            // Check approve670            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {671                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));672                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());673                appoved_transfer = opt_item.is_some();674                ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");675            }676677            // Transfer permissions check678            let target_collection = <Collection<T>>::get(collection_id);679            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 680                "Only item owner, collection owner and admins can modify items");681682            if target_collection.access == AccessMode::WhiteList {683                Self::check_white_list(collection_id, &sender)?;684                Self::check_white_list(collection_id, &recipient)?;685            }686687            // remove approve688            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))689                .into_iter().filter(|i| i.approved != sender.clone()).collect();690            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);691692693            match target_collection.mode694            {695                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,696                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,697                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,698                _ => ()699            };700701            Ok(())702        }703704        #[weight = 0]705        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {706707            // let no_perm_mes = "You do not have permissions to modify this collection";708            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);709            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));710            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);711712            // // on_nft_received  call713714            // Self::transfer(origin, collection_id, item_id, new_owner)?;715716            Ok(())717        }718719        #[weight = 0]720        pub fn set_offchain_schema(721            origin,722            collection_id: u64,723            schema: Vec<u8>724        ) -> DispatchResult {725            let sender = ensure_signed(origin)?;726            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;727728            let mut target_collection = <Collection<T>>::get(collection_id);729            target_collection.offchain_schema = schema;730            <Collection<T>>::insert(collection_id, target_collection);731732            Ok(())733        }734    }735}736737impl<T: Trait> Module<T> {738    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {739        let current_index = <ItemListIndex>::get(item.collection)740            .checked_add(1)741            .expect("Item list index id error");742        let itemcopy = item.clone();743        let owner = item.owner.clone();744        let value = item.value as u64;745746        Self::add_token_index(item.collection, current_index, owner.clone())?;747748        <ItemListIndex>::insert(item.collection, current_index);749        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);750751        // Update balance752        let new_balance = <Balance<T>>::get(item.collection, owner.clone())753            .checked_add(value)754            .unwrap();755        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);756757        Ok(())758    }759760    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {761        let current_index = <ItemListIndex>::get(item.collection)762            .checked_add(1)763            .expect("Item list index id error");764        let itemcopy = item.clone();765766        let value = item.owner.first().unwrap().fraction as u64;767        let owner = item.owner.first().unwrap().owner.clone();768769        Self::add_token_index(item.collection, current_index, owner.clone())?;770771        <ItemListIndex>::insert(item.collection, current_index);772        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);773774        // Update balance775        let new_balance = <Balance<T>>::get(item.collection, owner.clone())776            .checked_add(value)777            .unwrap();778        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);779780        Ok(())781    }782783    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {784        let current_index = <ItemListIndex>::get(item.collection)785            .checked_add(1)786            .expect("Item list index id error");787788        let item_owner = item.owner.clone();789        let collection_id = item.collection.clone();790        Self::add_token_index(collection_id, current_index, item.owner.clone())?;791792        <ItemListIndex>::insert(collection_id, current_index);793        <NftItemList<T>>::insert(collection_id, current_index, item);794795        // Update balance796        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())797            .checked_add(1)798            .unwrap();799        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);800801        Ok(())802    }803804    fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {805        ensure!(806            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),807            "Item does not exists"808        );809        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);810        let item = collection811            .owner812            .iter()813            .filter(|&i| i.owner == owner)814            .next()815            .unwrap();816        Self::remove_token_index(collection_id, item_id, owner.clone())?;817818        // remove approve list819        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));820821        // update balance822        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())823            .checked_sub(item.fraction as u64)824            .unwrap();825        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);826827        <ReFungibleItemList<T>>::remove(collection_id, item_id);828829        Ok(())830    }831832    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {833        ensure!(834            <NftItemList<T>>::contains_key(collection_id, item_id),835            "Item does not exists"836        );837        let item = <NftItemList<T>>::get(collection_id, item_id);838        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;839840        // remove approve list841        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));842843        // update balance844        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())845            .checked_sub(1)846            .unwrap();847        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);848        <NftItemList<T>>::remove(collection_id, item_id);849850        Ok(())851    }852853    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {854        ensure!(855            <FungibleItemList<T>>::contains_key(collection_id, item_id),856            "Item does not exists"857        );858        let item = <FungibleItemList<T>>::get(collection_id, item_id);859        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;860861        // remove approve list862        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));863864        // update balance865        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())866            .checked_sub(item.value as u64)867            .unwrap();868        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);869870        <FungibleItemList<T>>::remove(collection_id, item_id);871872        Ok(())873    }874875    fn collection_exists(collection_id: u64) -> DispatchResult {876        ensure!(877            <Collection<T>>::contains_key(collection_id),878            "This collection does not exist"879        );880        Ok(())881    }882883    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {884        Self::collection_exists(collection_id)?;885886        let target_collection = <Collection<T>>::get(collection_id);887        ensure!(888            subject == target_collection.owner,889            "You do not own this collection"890        );891892        Ok(())893    }894895    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {896897        let target_collection = <Collection<T>>::get(collection_id);898        let mut result: bool = subject == target_collection.owner;899        let exists = <AdminList<T>>::contains_key(collection_id);900901        if !result & exists {902            if <AdminList<T>>::get(collection_id).contains(&subject) {903                result = true904            }905        }906907        result908    }909910    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {911        912        Self::collection_exists(collection_id)?;913        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());914915        ensure!(result, "You do not have permissions to modify this collection");916        Ok(())917    }918919    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {920        let target_collection = <Collection<T>>::get(collection_id);921922        match target_collection.mode {923            CollectionMode::NFT(_) => {924                <NftItemList<T>>::get(collection_id, item_id).owner == subject925            }926            CollectionMode::Fungible(_) => {927                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject928            }929            CollectionMode::ReFungible(_, _) => {930                <ReFungibleItemList<T>>::get(collection_id, item_id)931                    .owner932                    .iter()933                    .any(|i| i.owner == subject)934            }935            CollectionMode::Invalid => false,936        }937    }938939    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {940941        let mes = "Address is not in white list";942        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);943        let wl = <WhiteList<T>>::get(collection_id);944        ensure!(wl.contains(address), mes);945946        Ok(())947    }948949    fn transfer_fungible(950        collection_id: u64,951        item_id: u64,952        value: u64,953        owner: T::AccountId,954        new_owner: T::AccountId,955    ) -> DispatchResult {956957        ensure!(958            <FungibleItemList<T>>::contains_key(collection_id, item_id),959            "Item not exists"960        );961962        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);963        let amount = full_item.value;964965        ensure!(amount >= value.into(), "Item balance not enouth");966967        // update balance968        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())969            .checked_sub(value)970            .unwrap();971        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);972973        let mut new_owner_account_id = 0;974        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());975        if new_owner_items.len() > 0 {976            new_owner_account_id = new_owner_items[0];977        }978979        let val64 = value.into();980981        // transfer982        if amount == val64 && new_owner_account_id == 0 {983            // change owner984            // new owner do not have account985            let mut new_full_item = full_item.clone();986            new_full_item.owner = new_owner.clone();987            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);988989            // update balance990            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())991                .checked_add(value)992                .unwrap();993            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);994995            // update index collection996            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;997        } else {998            let mut new_full_item = full_item.clone();999            new_full_item.value -= val64;10001001            // separate amount1002            if new_owner_account_id > 0 {1003                // new owner has account1004                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1005                item.value += val64;10061007                // update balance1008                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1009                    .checked_add(value)1010                    .unwrap();1011                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);10121013                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1014            } else {1015                // new owner do not have account1016                let item = FungibleItemType {1017                    collection: collection_id,1018                    owner: new_owner.clone(),1019                    value: val64,1020                };10211022                Self::add_fungible_item(item)?;1023            }10241025            if amount == val64 {1026                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;10271028                // remove approve list1029                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1030                <FungibleItemList<T>>::remove(collection_id, item_id);1031            }10321033            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1034        }10351036        Ok(())1037    }10381039    fn transfer_refungible(1040        collection_id: u64,1041        item_id: u64,1042        value: u64,1043        owner: T::AccountId,1044        new_owner: T::AccountId,1045    ) -> DispatchResult {10461047        ensure!(1048            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1049            "Item not exists"1050        );10511052        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1053        let item = full_item1054            .owner1055            .iter()1056            .filter(|i| i.owner == owner)1057            .next()1058            .unwrap();1059        let amount = item.fraction;10601061        ensure!(amount >= value.into(), "Item balance not enouth");10621063        // update balance1064        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1065            .checked_sub(value)1066            .unwrap();1067        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);10681069        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1070            .checked_add(value)1071            .unwrap();1072        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);10731074        let old_owner = item.owner.clone();1075        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1076        let val64 = value.into();10771078        // transfer1079        if amount == val64 && !new_owner_has_account {1080            // change owner1081            // new owner do not have account1082            let mut new_full_item = full_item.clone();1083            new_full_item1084                .owner1085                .iter_mut()1086                .find(|i| i.owner == owner)1087                .unwrap()1088                .owner = new_owner.clone();1089            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);10901091            // update index collection1092            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1093        } else {1094            let mut new_full_item = full_item.clone();1095            new_full_item1096                .owner1097                .iter_mut()1098                .find(|i| i.owner == owner)1099                .unwrap()1100                .fraction -= val64;11011102            // separate amount1103            if new_owner_has_account {1104                // new owner has account1105                new_full_item1106                    .owner1107                    .iter_mut()1108                    .find(|i| i.owner == new_owner)1109                    .unwrap()1110                    .fraction += val64;1111            } else {1112                // new owner do not have account1113                new_full_item.owner.push(Ownership {1114                    owner: new_owner.clone(),1115                    fraction: val64,1116                });1117                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1118            }11191120            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1121        }11221123        Ok(())1124    }11251126    fn transfer_nft(1127        collection_id: u64,1128        item_id: u64,1129        sender: T::AccountId,1130        new_owner: T::AccountId,1131    ) -> DispatchResult {1132    1133        ensure!(1134            <NftItemList<T>>::contains_key(collection_id, item_id),1135            "Item not exists"1136        );11371138        let mut item = <NftItemList<T>>::get(collection_id, item_id);11391140        ensure!(1141            sender == item.owner,1142            "sender parameter and item owner must be equal"1143        );11441145        // update balance1146        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1147            .checked_sub(1)1148            .unwrap();1149        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);11501151        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1152            .checked_add(1)1153            .unwrap();1154        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);11551156        // change owner1157        let old_owner = item.owner.clone();1158        item.owner = new_owner.clone();1159        <NftItemList<T>>::insert(collection_id, item_id, item);11601161        // update index collection1162        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;11631164        // reset approved list1165        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1166        Ok(())1167    }11681169    fn init_collection(item: &CollectionType<T::AccountId>){11701171                // check params1172                assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");1173                assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");1174                assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");1175                assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");1176    1177                // Generate next collection ID1178                let next_id = CreatedCollectionCount::get()1179                    .checked_add(1)1180                    .expect("collection id error");1181    1182                CreatedCollectionCount::put(next_id);  1183    }11841185    fn init_nft_token(item: &NftItemType<T::AccountId>){11861187        let current_index = <ItemListIndex>::get(item.collection)1188            .checked_add(1)1189            .expect("Item list index id error");11901191        let item_owner = item.owner.clone();1192        let collection_id = item.collection.clone();1193        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();11941195        <ItemListIndex>::insert(collection_id, current_index);11961197        // Update balance1198        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1199            .checked_add(1)1200            .unwrap();1201        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1202    }12031204    fn init_fungible_token(item: &FungibleItemType<T::AccountId>){12051206        let current_index = <ItemListIndex>::get(item.collection)1207            .checked_add(1)1208            .expect("Item list index id error");1209        let owner = item.owner.clone();1210        let value = item.value as u64;12111212        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12131214        <ItemListIndex>::insert(item.collection, current_index);12151216        // Update balance1217        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1218            .checked_add(value)1219            .unwrap();1220        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1221    }12221223    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){12241225        let current_index = <ItemListIndex>::get(item.collection)1226            .checked_add(1)1227            .expect("Item list index id error");12281229        let value = item.owner.first().unwrap().fraction as u64;1230        let owner = item.owner.first().unwrap().owner.clone();12311232        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();12331234        <ItemListIndex>::insert(item.collection, current_index);12351236        // Update balance1237        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1238            .checked_add(value)1239            .unwrap();1240        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1241    }12421243    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {1244        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1245        if list_exists {1246            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1247            let item_contains = list.contains(&item_index.clone());12481249            if !item_contains {1250                list.push(item_index.clone());1251            }12521253            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1254        } else {1255            let mut itm = Vec::new();1256            itm.push(item_index.clone());1257            <AddressTokens<T>>::insert(collection_id, owner, itm);1258        }12591260        Ok(())1261    }12621263    fn remove_token_index(1264        collection_id: u64,1265        item_index: u64,1266        owner: T::AccountId,1267    ) -> DispatchResult {1268        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1269        if list_exists {1270            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1271            let item_contains = list.contains(&item_index.clone());12721273            if item_contains {1274                list.retain(|&item| item != item_index);1275                <AddressTokens<T>>::insert(collection_id, owner, list);1276            }1277        }12781279        Ok(())1280    }12811282    fn move_token_index(1283        collection_id: u64,1284        item_index: u64,1285        old_owner: T::AccountId,1286        new_owner: T::AccountId,1287    ) -> DispatchResult {1288        Self::remove_token_index(collection_id, item_index, old_owner)?;1289        Self::add_token_index(collection_id, item_index, new_owner)?;12901291        Ok(())1292    }1293}12941295////////////////////////////////////////////////////////////////////////////////////////////////////1296// Economic models1297// #region12981299/// Fee multiplier.1300pub type Multiplier = FixedU128;13011302type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1303    <T as system::Trait>::AccountId,1304>>::Balance;1305type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1306    <T as system::Trait>::AccountId,1307>>::NegativeImbalance;13081309/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1310/// in the queue.1311#[derive(Encode, Decode, Clone, Eq, PartialEq)]1312pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1313    #[codec(compact)] BalanceOf<T>,1314);13151316impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1317    for ChargeTransactionPayment<T>1318{1319    #[cfg(feature = "std")]1320    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1321        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1322    }1323    #[cfg(not(feature = "std"))]1324    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1325        Ok(())1326    }1327}13281329impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1330where1331    T::Call:1332        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1333    BalanceOf<T>: Send + Sync + FixedPointOperand,1334{1335    /// utility constructor. Used only in client/factory code.1336    pub fn from(fee: BalanceOf<T>) -> Self {1337        Self(fee)1338    }13391340    pub fn traditional_fee(1341        len: usize,1342        info: &DispatchInfoOf<T::Call>,1343        tip: BalanceOf<T>,1344    ) -> BalanceOf<T>1345    where1346        T::Call: Dispatchable<Info = DispatchInfo>,1347    {1348        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1349    }13501351    fn withdraw_fee(1352        &self,1353        who: &T::AccountId,1354        call: &T::Call,1355        info: &DispatchInfoOf<T::Call>,1356        len: usize,1357    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1358        let tip = self.0;13591360        // Set fee based on call type. Creating collection costs 1 Unique.1361        // All other transactions have traditional fees so far1362        let fee = match call.is_sub_type() {1363            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1364            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1365                                                        // _ => <BalanceOf<T>>::from(100)1366        };13671368        // Determine who is paying transaction fee based on ecnomic model1369        // Parse call to extract collection ID and access collection sponsor1370        let sponsor: T::AccountId = match call.is_sub_type() {1371            Some(Call::create_item(collection_id, _properties, _owner)) => {1372                <Collection<T>>::get(collection_id).sponsor1373            }1374            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1375                <Collection<T>>::get(collection_id).sponsor1376            }13771378            _ => T::AccountId::default(),1379        };13801381        let mut who_pays_fee: T::AccountId = sponsor.clone();1382        if sponsor == T::AccountId::default() {1383            who_pays_fee = who.clone();1384        }13851386        // Only mess with balances if fee is not zero.1387        if fee.is_zero() {1388            return Ok((fee, None));1389        }13901391        match <T as transaction_payment::Trait>::Currency::withdraw(1392            &who_pays_fee,1393            fee,1394            if tip.is_zero() {1395                WithdrawReason::TransactionPayment.into()1396            } else {1397                WithdrawReason::TransactionPayment | WithdrawReason::Tip1398            },1399            ExistenceRequirement::KeepAlive,1400        ) {1401            Ok(imbalance) => Ok((fee, Some(imbalance))),1402            Err(_) => Err(InvalidTransaction::Payment.into()),1403        }1404    }1405}14061407impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1408    for ChargeTransactionPayment<T>1409where1410    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1411    T::Call:1412        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1413{1414    const IDENTIFIER: &'static str = "ChargeTransactionPayment";1415    type AccountId = T::AccountId;1416    type Call = T::Call;1417    type AdditionalSigned = ();1418    type Pre = (1419        BalanceOf<T>,1420        Self::AccountId,1421        Option<NegativeImbalanceOf<T>>,1422        BalanceOf<T>,1423    );1424    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1425        Ok(())1426    }14271428    fn validate(1429        &self,1430        who: &Self::AccountId,1431        call: &Self::Call,1432        info: &DispatchInfoOf<Self::Call>,1433        len: usize,1434    ) -> TransactionValidity {1435        let (fee, _) = self.withdraw_fee(who, call, info, len)?;14361437        let mut r = ValidTransaction::default();1438        // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which1439        // will be a bit more than setting the priority to tip. For now, this is enough.1440        r.priority = fee.saturated_into::<TransactionPriority>();1441        Ok(r)1442    }14431444    fn pre_dispatch(1445        self,1446        who: &Self::AccountId,1447        call: &Self::Call,1448        info: &DispatchInfoOf<Self::Call>,1449        len: usize,1450    ) -> Result<Self::Pre, TransactionValidityError> {1451        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1452        Ok((self.0, who.clone(), imbalance, fee))1453    }14541455    fn post_dispatch(1456        pre: Self::Pre,1457        info: &DispatchInfoOf<Self::Call>,1458        post_info: &PostDispatchInfoOf<Self::Call>,1459        len: usize,1460        _result: &DispatchResult,1461    ) -> Result<(), TransactionValidityError> {1462        let (tip, who, imbalance, fee) = pre;1463        if let Some(payed) = imbalance {1464            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1465                len as u32, info, post_info, tip,1466            );1467            let refund = fee.saturating_sub(actual_fee);1468            let actual_payment =1469                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1470                    &who, refund,1471                ) {1472                    Ok(refund_imbalance) => {1473                        // The refund cannot be larger than the up front payed max weight.1474                        // `PostDispatchInfo::calc_unspent` guards against such a case.1475                        match payed.offset(refund_imbalance) {1476                            Ok(actual_payment) => actual_payment,1477                            Err(_) => return Err(InvalidTransaction::Payment.into()),1478                        }1479                    }1480                    // We do not recreate the account using the refund. The up front payment1481                    // is gone in that case.1482                    Err(_) => payed,1483                };1484            let imbalances = actual_payment.split(tip);1485            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1486                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1487            );1488        }1489        Ok(())1490    }1491}1492// #endregion
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use serde::*;56use codec::{Decode, Encode};7pub use frame_support::{8    construct_runtime, decl_event, decl_module, decl_storage,9    dispatch::DispatchResult,10    ensure, parameter_types,11    traits::{12        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,13        Randomness, WithdrawReason,14    },15    weights::{16        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},17        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,18        WeightToFeePolynomial,19    },20    IsSubType, StorageValue,21};2223use frame_system::{self as system, ensure_signed};24use sp_runtime::sp_std::prelude::Vec;25use sp_runtime::{26    traits::{27        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,28        SignedExtension, Zero,29    },30    transaction_validity::{31        InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,32        ValidTransaction,33    },34    FixedPointOperand, FixedU128,35};3637#[cfg(test)]38mod mock;3940#[cfg(test)]41mod tests;4243// Structs44// #region4546#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]47#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]48pub enum CollectionMode {49    Invalid,50    // custom data size51    NFT(u32),52    // decimal points53    Fungible(u32),54    // custom data size and decimal points55    ReFungible(u32, u32),56}5758impl Into<u8> for CollectionMode {59    fn into(self) -> u8 {60        match self {61            CollectionMode::Invalid => 0,62            CollectionMode::NFT(_) => 1,63            CollectionMode::Fungible(_) => 2,64            CollectionMode::ReFungible(_, _) => 3,65        }66    }67}6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum AccessMode {72    Normal,73    WhiteList,74}75impl Default for AccessMode {76    fn default() -> Self {77        Self::Normal78    }79}8081impl Default for CollectionMode {82    fn default() -> Self {83        Self::Invalid84    }85}8687#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]88#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]89pub struct Ownership<AccountId> {90    pub owner: AccountId,91    pub fraction: u128,92}9394#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]96pub struct CollectionType<AccountId> {97    pub owner: AccountId,98    pub mode: CollectionMode,99    pub access: AccessMode,100    pub decimal_points: u32,101    pub name: Vec<u16>,        // 64 include null escape char102    pub description: Vec<u16>, // 256 include null escape char103    pub token_prefix: Vec<u8>, // 16 include null escape char104    pub custom_data_size: u32,105    pub mint_mode: bool,106    pub offchain_schema: Vec<u8>,107    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender108    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship109}110111#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub struct CollectionAdminsType<AccountId> {114    pub admin: AccountId,115    pub collection_id: u64,116}117118#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]119#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]120pub struct NftItemType<AccountId> {121    pub collection: u64,122    pub owner: AccountId,123    pub data: Vec<u8>,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct FungibleItemType<AccountId> {129    pub collection: u64,130    pub owner: AccountId,131    pub value: u128,132}133134#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub struct ReFungibleItemType<AccountId> {137    pub collection: u64,138    pub owner: Vec<Ownership<AccountId>>,139    pub 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}159160pub trait Trait: system::Trait {161    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;162}163164// #endregion165166decl_storage! {167    trait Store for Module<T: Trait> as Nft {168169        // Private members170        NextCollectionID: u64;171        CreatedCollectionCount: u64;172        ChainVersion: u64;173        ItemListIndex: map hasher(blake2_128_concat) u64 => u64;174175        pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;176        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;177        pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;178179        /// Balance owner per collection map180        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;181182        /// second parameter: item id + owner account id183        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;184185        /// Item collections186        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;187        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;188        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;189190        /// Index list191        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;192193        // Sponsorship194        pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;195        pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;196    }197    add_extra_genesis {198        build(|config: &GenesisConfig<T>| {199			// Modification of storage200            for (_num, _c) in &config.collection {201                <Module<T>>::init_collection(_c);202            }203204            for (_num, _q, _i) in &config.nft_item_id {205                <Module<T>>::init_nft_token(_i);206            }207208            for (_num, _q, _i) in &config.fungible_item_id {209                <Module<T>>::init_fungible_token(_i);210            }211212            for (_num, _q, _i) in &config.refungible_item_id {213                <Module<T>>::init_refungible_token(_i);214            }215		})216    }217}218219decl_event!(220    pub enum Event<T>221    where222        AccountId = <T as system::Trait>::AccountId,223    {224        /// New collection was created225        /// 226        /// # Arguments227        /// 228        /// * collection_id: Globally unique identifier of newly created collection.229        /// 230        /// * mode: [CollectionMode] converted into u8.231        /// 232        /// * account_id: Collection owner.233        Created(u64, u8, AccountId),234235        /// New item was created.236        /// 237        /// # Arguments238        /// 239        /// * collection_id: Id of the collection where item was created.240        /// 241        /// * item_id: Id of an item. Unique within the collection.242        ItemCreated(u64, u64),243244        /// Collection item was burned.245        /// 246        /// # Arguments247        /// 248        /// collection_id.249        /// 250        /// item_id: Identifier of burned NFT.251        ItemDestroyed(u64, u64),252    }253);254255decl_module! {256    pub struct Module<T: Trait> for enum Call where origin: T::Origin {257258        fn deposit_event() = default;259260        fn on_initialize(now: T::BlockNumber) -> Weight {261262            if ChainVersion::get() < 2263            {264                let value = NextCollectionID::get();265                CreatedCollectionCount::put(value);266                ChainVersion::put(2);267            }268269            0270        }271272        /// 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.273        /// 274        /// # Permissions275        /// 276        /// * Anyone.277        /// 278        /// # Arguments279        /// 280        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.281        /// 282        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.283        /// 284        /// * token_prefix: UTF-8 string with token prefix.285        /// 286        /// * mode: [CollectionMode] collection type and type dependent data.287        // returns collection ID288        #[weight = 0]289        pub fn create_collection(origin,290                                 collection_name: Vec<u16>,291                                 collection_description: Vec<u16>,292                                 token_prefix: Vec<u8>,293                                 mode: CollectionMode) -> DispatchResult {294295            // Anyone can create a collection296            let who = ensure_signed(origin)?;297            let custom_data_size = match mode {298                CollectionMode::NFT(size) => size,299                CollectionMode::ReFungible(size, _) => size,300                _ => 0301            };302303            let decimal_points = match mode {304                CollectionMode::Fungible(points) => points,305                CollectionMode::ReFungible(_, points) => points,306                _ => 0307            };308309            // check params310            ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");311312            let mut name = collection_name.to_vec();313            name.push(0);314            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");315316            let mut description = collection_description.to_vec();317            description.push(0);318            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");319320            let mut prefix = token_prefix.to_vec();321            prefix.push(0);322            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");323324            // Generate next collection ID325            let next_id = CreatedCollectionCount::get()326                .checked_add(1)327                .expect("collection id error");328329            CreatedCollectionCount::put(next_id);330331            // Create new collection332            let new_collection = CollectionType {333                owner: who.clone(),334                name: name,335                mode: mode.clone(),336                mint_mode: false,337                access: AccessMode::Normal,338                description: description,339                decimal_points: decimal_points,340                token_prefix: prefix,341                offchain_schema: Vec::new(),342                custom_data_size: custom_data_size,343                sponsor: T::AccountId::default(),344                unconfirmed_sponsor: T::AccountId::default(),345            };346347            // Add new collection to map348            <Collection<T>>::insert(next_id, new_collection);349350            // call event351            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));352353            Ok(())354        }355356        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.357        /// 358        /// # Permissions359        /// 360        /// * Collection Owner.361        /// 362        /// # Arguments363        /// 364        /// * collection_id: collection to destroy.365        #[weight = 0]366        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {367368            let sender = ensure_signed(origin)?;369            Self::check_owner_permissions(collection_id, sender)?;370371            // TODO Items remove372            <AddressTokens<T>>::remove_prefix(collection_id);373            <ApprovedList<T>>::remove_prefix(collection_id);374            <Balance<T>>::remove_prefix(collection_id);375            <ItemListIndex>::remove(collection_id);376            <AdminList<T>>::remove(collection_id);377            <Collection<T>>::remove(collection_id);378            <WhiteList<T>>::remove(collection_id);379380            Ok(())381        }382383        /// Add an address to white list.384        /// 385        /// # Permissions386        /// 387        /// * Collection Owner388        /// * Collection Admin389        /// 390        /// # Arguments391        /// 392        /// * collection_id.393        /// 394        /// * address.395        #[weight = 0]396        pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{397398            let sender = ensure_signed(origin)?;399            Self::check_owner_or_admin_permissions(collection_id, sender)?;400401            let mut white_list_collection: Vec<T::AccountId>;402            if <WhiteList<T>>::contains_key(collection_id) {403                white_list_collection = <WhiteList<T>>::get(collection_id);404                if !white_list_collection.contains(&address.clone())405                {406                    white_list_collection.push(address.clone());407                }408            }409            else {410                white_list_collection = Vec::new();411                white_list_collection.push(address.clone());412            }413414            <WhiteList<T>>::insert(collection_id, white_list_collection);415            Ok(())416        }417418        /// Remove an address from white list.419        /// 420        /// # Permissions421        /// 422        /// * Collection Owner423        /// * Collection Admin424        /// 425        /// # Arguments426        /// 427        /// * collection_id.428        /// 429        /// * address.430        #[weight = 0]431        pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{432433            let sender = ensure_signed(origin)?;434            Self::check_owner_or_admin_permissions(collection_id, sender)?;435436            if <WhiteList<T>>::contains_key(collection_id) {437                let mut white_list_collection = <WhiteList<T>>::get(collection_id);438                if white_list_collection.contains(&address.clone())439                {440                    white_list_collection.retain(|i| *i != address.clone());441                    <WhiteList<T>>::insert(collection_id, white_list_collection);442                }443            }444445            Ok(())446        }447448        /// Toggle between normal and white list access for the methods with access for `Anyone`.449        /// 450        /// # Permissions451        /// 452        /// * Collection Owner.453        /// 454        /// # Arguments455        /// 456        /// * collection_id.457        /// 458        /// * mode: [AccessMode]459        #[weight = 0]460        pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult461        {462            let sender = ensure_signed(origin)?;463464            Self::check_owner_permissions(collection_id, sender)?;465            let mut target_collection = <Collection<T>>::get(collection_id);466            target_collection.access = mode;467            <Collection<T>>::insert(collection_id, target_collection);468469            Ok(())470        }471472        /// Allows Anyone to create tokens if:473        /// * White List is enabled, and474        /// * Address is added to white list, and475        /// * This method was called with True parameter476        /// 477        /// # Permissions478        /// * Collection Owner479        ///480        /// # Arguments481        /// 482        /// * collection_id.483        /// 484        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.485        #[weight = 0]486        pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult487        {488            let sender = ensure_signed(origin)?;489490            Self::check_owner_permissions(collection_id, sender)?;491            let mut target_collection = <Collection<T>>::get(collection_id);492            target_collection.mint_mode = mint_permission;493            <Collection<T>>::insert(collection_id, target_collection);494495            Ok(())496        }497498        /// Change the owner of the collection.499        /// 500        /// # Permissions501        /// 502        /// * Collection Owner.503        /// 504        /// # Arguments505        /// 506        /// * collection_id.507        /// 508        /// * new_owner.509        #[weight = 0]510        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {511512            let sender = ensure_signed(origin)?;513            Self::check_owner_permissions(collection_id, sender)?;514            let mut target_collection = <Collection<T>>::get(collection_id);515            target_collection.owner = new_owner;516            <Collection<T>>::insert(collection_id, target_collection);517518            Ok(())519        }520521        /// Adds an admin of the Collection.522        /// 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. 523        /// 524        /// # Permissions525        /// 526        /// * Collection Owner.527        /// * Collection Admin.528        /// 529        /// # Arguments530        /// 531        /// * collection_id: ID of the Collection to add admin for.532        /// 533        /// * new_admin_id: Address of new admin to add.534        #[weight = 0]535        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {536537            let sender = ensure_signed(origin)?;538            Self::check_owner_or_admin_permissions(collection_id, sender)?;539            let mut admin_arr: Vec<T::AccountId> = Vec::new();540541            if <AdminList<T>>::contains_key(collection_id)542            {543                admin_arr = <AdminList<T>>::get(collection_id);544                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");545            }546547            admin_arr.push(new_admin_id);548            <AdminList<T>>::insert(collection_id, admin_arr);549550            Ok(())551        }552553        /// 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.554        ///555        /// # Permissions556        /// 557        /// * Collection Owner.558        /// * Collection Admin.559        /// 560        /// # Arguments561        /// 562        /// * collection_id: ID of the Collection to remove admin for.563        /// 564        /// * account_id: Address of admin to remove.565        #[weight = 0]566        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {567568            let sender = ensure_signed(origin)?;569            Self::check_owner_or_admin_permissions(collection_id, sender)?;570571            if <AdminList<T>>::contains_key(collection_id)572            {573                let mut admin_arr = <AdminList<T>>::get(collection_id);574                admin_arr.retain(|i| *i != account_id);575                <AdminList<T>>::insert(collection_id, admin_arr);576            }577578            Ok(())579        }580581        /// # Permissions582        /// 583        /// * Collection Owner584        /// 585        /// # Arguments586        /// 587        /// * collection_id.588        /// 589        /// * new_sponsor.590        #[weight = 0]591        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {592593            let sender = ensure_signed(origin)?;594            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");595596            let mut target_collection = <Collection<T>>::get(collection_id);597            ensure!(sender == target_collection.owner, "You do not own this collection");598599            target_collection.unconfirmed_sponsor = new_sponsor;600            <Collection<T>>::insert(collection_id, target_collection);601602            Ok(())603        }604605        /// # Permissions606        /// 607        /// * Sponsor.608        /// 609        /// # Arguments610        /// 611        /// * collection_id.612        #[weight = 0]613        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {614615            let sender = ensure_signed(origin)?;616            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");617618            let mut target_collection = <Collection<T>>::get(collection_id);619            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");620621            target_collection.sponsor = target_collection.unconfirmed_sponsor;622            target_collection.unconfirmed_sponsor = T::AccountId::default();623            <Collection<T>>::insert(collection_id, target_collection);624625            Ok(())626        }627628        /// Switch back to pay-per-own-transaction model.629        ///630        /// # Permissions631        ///632        /// * Collection owner.633        /// 634        /// # Arguments635        /// 636        /// * collection_id.637        #[weight = 0]638        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {639640            let sender = ensure_signed(origin)?;641            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");642643            let mut target_collection = <Collection<T>>::get(collection_id);644            ensure!(sender == target_collection.owner, "You do not own this collection");645646            target_collection.sponsor = T::AccountId::default();647            <Collection<T>>::insert(collection_id, target_collection);648649            Ok(())650        }651652        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.653        /// 654        /// # Permissions655        /// 656        /// * Collection Owner.657        /// * Collection Admin.658        /// * Anyone if659        ///     * White List is enabled, and660        ///     * Address is added to white list, and661        ///     * MintPermission is enabled (see SetMintPermission method)662        /// 663        /// # Arguments664        /// 665        /// * collection_id: ID of the collection.666        /// 667        /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.668        /// 669        /// * owner: Address, initial owner of the NFT.670        #[weight = 0]671        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {672673            let sender = ensure_signed(origin)?;674            Self::collection_exists(collection_id)?;675            let target_collection = <Collection<T>>::get(collection_id);676677            if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {678                ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");679                Self::check_white_list(collection_id, &owner)?;680                Self::check_white_list(collection_id, &sender)?;681            }682683            match target_collection.mode684            {685                CollectionMode::NFT(_) => {686687                    // check size688                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");689690                    // Create nft item691                    let item = NftItemType {692                        collection: collection_id,693                        owner: owner,694                        data: properties.clone(),695                    };696697                    Self::add_nft_item(item)?;698699                },700                CollectionMode::Fungible(_) => {701702                    // check size703                    ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");704705                    let item = FungibleItemType {706                        collection: collection_id,707                        owner: owner,708                        value: (10 as u128).pow(target_collection.decimal_points)709                    };710711                    Self::add_fungible_item(item)?;712                },713                CollectionMode::ReFungible(_, _) => {714715                    // check size716                    ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");717718                    let mut owner_list = Vec::new();719                    let value = (10 as u128).pow(target_collection.decimal_points);720                    owner_list.push(Ownership {owner: owner.clone(), fraction: value});721722                    let item = ReFungibleItemType {723                        collection: collection_id,724                        owner: owner_list,725                        data: properties.clone()726                    };727728                    Self::add_refungible_item(item)?;729                },730                _ => { ensure!(1 == 0,"just error"); }731732            };733734            // call event735            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));736737            Ok(())738        }739740        /// Destroys a concrete instance of NFT.741        /// 742        /// # Permissions743        /// 744        /// * Collection Owner.745        /// * Collection Admin.746        /// * Current NFT Owner.747        /// 748        /// # Arguments749        /// 750        /// * collection_id: ID of the collection.751        /// 752        /// * item_id: ID of NFT to burn.753        #[weight = 0]754        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {755756            let sender = ensure_signed(origin)?;757            Self::collection_exists(collection_id)?;758759            // Transfer permissions check760            let target_collection = <Collection<T>>::get(collection_id);761            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 762                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 763                "Only item owner, collection owner and admins can modify item");764765            if target_collection.access == AccessMode::WhiteList {766                Self::check_white_list(collection_id, &sender)?;767            }768769            match target_collection.mode770            {771                CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,772                CollectionMode::Fungible(_)  => Self::burn_fungible_item(collection_id, item_id)?,773                CollectionMode::ReFungible(_, _)  => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,774                _ => ()775            };776777            // call event778            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));779780            Ok(())781        }782783        /// Change ownership of the token.784        /// 785        /// # Permissions786        /// 787        /// * Collection Owner788        /// * Collection Admin789        /// * Current NFT owner790        ///791        /// # Arguments792        /// 793        /// * recipient: Address of token recipient.794        /// 795        /// * collection_id.796        /// 797        /// * item_id: ID of the item798        ///     * Non-Fungible Mode: Required.799        ///     * Fungible Mode: Ignored.800        ///     * Re-Fungible Mode: Required.801        /// 802        /// * value: Amount to transfer.803        ///     * Non-Fungible Mode: Ignored804        ///     * Fungible Mode: Must specify transferred amount805        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)806        #[weight = 0]807        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {808809            let sender = ensure_signed(origin)?;810811            // Transfer permissions check812            let target_collection = <Collection<T>>::get(collection_id);813            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 814                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 815                "Only item owner, collection owner and admins can modify item");816817            if target_collection.access == AccessMode::WhiteList {818                Self::check_white_list(collection_id, &sender)?;819                Self::check_white_list(collection_id, &recipient)?;820            }821822            match target_collection.mode823            {824                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,825                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,826                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,827                _ => ()828            };829830            Ok(())831        }832833        /// Set, change, or remove approved address to transfer the ownership of the NFT.834        /// 835        /// # Permissions836        /// 837        /// * Collection Owner838        /// * Collection Admin839        /// * Current NFT owner840        /// 841        /// # Arguments842        /// 843        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).844        /// 845        /// * collection_id.846        /// 847        /// * item_id: ID of the item.848        #[weight = 0]849        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {850851            let sender = ensure_signed(origin)?;852853            // Transfer permissions check854            let target_collection = <Collection<T>>::get(collection_id);855            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) || 856                Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 857                "Only item owner, collection owner and admins can approve");858859            if target_collection.access == AccessMode::WhiteList {860                Self::check_white_list(collection_id, &sender)?;861                Self::check_white_list(collection_id, &approved)?;862            }863864            // amount param stub865            let amount = 100000000;866867            let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));868            if list_exists {869870                let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));871                let item_contains = list.iter().any(|i| i.approved == approved);872873                if !item_contains {874                    list.push(ApprovePermissions { approved: approved.clone(), amount: amount });875                    <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);876                }877            } else {878879                let mut list = Vec::new();880                list.push(ApprovePermissions { approved: approved.clone(), amount: amount });881                <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);882            }883884            Ok(())885        }886        887        /// 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.888        /// 889        /// # Permissions890        /// * Collection Owner891        /// * Collection Admin892        /// * Current NFT owner893        /// * Address approved by current NFT owner894        /// 895        /// # Arguments896        /// 897        /// * from: Address that owns token.898        /// 899        /// * recipient: Address of token recipient.900        /// 901        /// * collection_id.902        /// 903        /// * item_id: ID of the item.904        /// 905        /// * value: Amount to transfer.906        #[weight = 0]907        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {908909            let sender = ensure_signed(origin)?;910            let mut appoved_transfer = false;911912            // Check approve913            if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {914                let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));915                let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());916                appoved_transfer = opt_item.is_some();917                ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");918            }919920            // Transfer permissions check921            let target_collection = <Collection<T>>::get(collection_id);922            ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()), 923                "Only item owner, collection owner and admins can modify items");924925            if target_collection.access == AccessMode::WhiteList {926                Self::check_white_list(collection_id, &sender)?;927                Self::check_white_list(collection_id, &recipient)?;928            }929930            // remove approve931            let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))932                .into_iter().filter(|i| i.approved != sender.clone()).collect();933            <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);934935936            match target_collection.mode937            {938                CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,939                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,940                CollectionMode::ReFungible(_, _)  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,941                _ => ()942            };943944            Ok(())945        }946947        ///948        #[weight = 0]949        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {950951            // let no_perm_mes = "You do not have permissions to modify this collection";952            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);953            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));954            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);955956            // // on_nft_received  call957958            // Self::transfer(origin, collection_id, item_id, new_owner)?;959960            Ok(())961        }962963        /// Set off-chain data schema.964        /// 965        /// # Permissions966        /// 967        /// * Collection Owner968        /// * Collection Admin969        /// 970        /// # Arguments971        /// 972        /// * collection_id.973        /// 974        /// * schema: String representing the offchain data schema.975        #[weight = 0]976        pub fn set_offchain_schema(977            origin,978            collection_id: u64,979            schema: Vec<u8>980        ) -> DispatchResult {981            let sender = ensure_signed(origin)?;982            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;983984            let mut target_collection = <Collection<T>>::get(collection_id);985            target_collection.offchain_schema = schema;986            <Collection<T>>::insert(collection_id, target_collection);987988            Ok(())989        }990    }991}992993impl<T: Trait> Module<T> {994    fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {995        let current_index = <ItemListIndex>::get(item.collection)996            .checked_add(1)997            .expect("Item list index id error");998        let itemcopy = item.clone();999        let owner = item.owner.clone();1000        let value = item.value as u64;10011002        Self::add_token_index(item.collection, current_index, owner.clone())?;10031004        <ItemListIndex>::insert(item.collection, current_index);1005        <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);10061007        // Update balance1008        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1009            .checked_add(value)1010            .unwrap();1011        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);10121013        Ok(())1014    }10151016    fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1017        let current_index = <ItemListIndex>::get(item.collection)1018            .checked_add(1)1019            .expect("Item list index id error");1020        let itemcopy = item.clone();10211022        let value = item.owner.first().unwrap().fraction as u64;1023        let owner = item.owner.first().unwrap().owner.clone();10241025        Self::add_token_index(item.collection, current_index, owner.clone())?;10261027        <ItemListIndex>::insert(item.collection, current_index);1028        <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);10291030        // Update balance1031        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1032            .checked_add(value)1033            .unwrap();1034        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);10351036        Ok(())1037    }10381039    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1040        let current_index = <ItemListIndex>::get(item.collection)1041            .checked_add(1)1042            .expect("Item list index id error");10431044        let item_owner = item.owner.clone();1045        let collection_id = item.collection.clone();1046        Self::add_token_index(collection_id, current_index, item.owner.clone())?;10471048        <ItemListIndex>::insert(collection_id, current_index);1049        <NftItemList<T>>::insert(collection_id, current_index, item);10501051        // Update balance1052        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1053            .checked_add(1)1054            .unwrap();1055        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);10561057        Ok(())1058    }10591060    fn burn_refungible_item(collection_id: u64, item_id: u64, owner: T::AccountId) -> DispatchResult {1061        ensure!(1062            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1063            "Item does not exists"1064        );1065        let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1066        let item = collection1067            .owner1068            .iter()1069            .filter(|&i| i.owner == owner)1070            .next()1071            .unwrap();1072        Self::remove_token_index(collection_id, item_id, owner.clone())?;10731074        // remove approve list1075        <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));10761077        // update balance1078        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1079            .checked_sub(item.fraction as u64)1080            .unwrap();1081        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);10821083        <ReFungibleItemList<T>>::remove(collection_id, item_id);10841085        Ok(())1086    }10871088    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1089        ensure!(1090            <NftItemList<T>>::contains_key(collection_id, item_id),1091            "Item does not exists"1092        );1093        let item = <NftItemList<T>>::get(collection_id, item_id);1094        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;10951096        // remove approve list1097        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));10981099        // update balance1100        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1101            .checked_sub(1)1102            .unwrap();1103        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1104        <NftItemList<T>>::remove(collection_id, item_id);11051106        Ok(())1107    }11081109    fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1110        ensure!(1111            <FungibleItemList<T>>::contains_key(collection_id, item_id),1112            "Item does not exists"1113        );1114        let item = <FungibleItemList<T>>::get(collection_id, item_id);1115        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;11161117        // remove approve list1118        <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));11191120        // update balance1121        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1122            .checked_sub(item.value as u64)1123            .unwrap();1124        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);11251126        <FungibleItemList<T>>::remove(collection_id, item_id);11271128        Ok(())1129    }11301131    fn collection_exists(collection_id: u64) -> DispatchResult {1132        ensure!(1133            <Collection<T>>::contains_key(collection_id),1134            "This collection does not exist"1135        );1136        Ok(())1137    }11381139    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1140        Self::collection_exists(collection_id)?;11411142        let target_collection = <Collection<T>>::get(collection_id);1143        ensure!(1144            subject == target_collection.owner,1145            "You do not own this collection"1146        );11471148        Ok(())1149    }11501151    fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {11521153        let target_collection = <Collection<T>>::get(collection_id);1154        let mut result: bool = subject == target_collection.owner;1155        let exists = <AdminList<T>>::contains_key(collection_id);11561157        if !result & exists {1158            if <AdminList<T>>::get(collection_id).contains(&subject) {1159                result = true1160            }1161        }11621163        result1164    }11651166    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1167        1168        Self::collection_exists(collection_id)?;1169        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());11701171        ensure!(result, "You do not have permissions to modify this collection");1172        Ok(())1173    }11741175    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1176        let target_collection = <Collection<T>>::get(collection_id);11771178        match target_collection.mode {1179            CollectionMode::NFT(_) => {1180                <NftItemList<T>>::get(collection_id, item_id).owner == subject1181            }1182            CollectionMode::Fungible(_) => {1183                <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1184            }1185            CollectionMode::ReFungible(_, _) => {1186                <ReFungibleItemList<T>>::get(collection_id, item_id)1187                    .owner1188                    .iter()1189                    .any(|i| i.owner == subject)1190            }1191            CollectionMode::Invalid => false,1192        }1193    }11941195    fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {11961197        let mes = "Address is not in white list";1198        ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1199        let wl = <WhiteList<T>>::get(collection_id);1200        ensure!(wl.contains(address), mes);12011202        Ok(())1203    }12041205    fn transfer_fungible(1206        collection_id: u64,1207        item_id: u64,1208        value: u64,1209        owner: T::AccountId,1210        new_owner: T::AccountId,1211    ) -> DispatchResult {12121213        ensure!(1214            <FungibleItemList<T>>::contains_key(collection_id, item_id),1215            "Item not exists"1216        );12171218        let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1219        let amount = full_item.value;12201221        ensure!(amount >= value.into(), "Item balance not enouth");12221223        // update balance1224        let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1225            .checked_sub(value)1226            .unwrap();1227        <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);12281229        let mut new_owner_account_id = 0;1230        let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1231        if new_owner_items.len() > 0 {1232            new_owner_account_id = new_owner_items[0];1233        }12341235        let val64 = value.into();12361237        // transfer1238        if amount == val64 && new_owner_account_id == 0 {1239            // change owner1240            // new owner do not have account1241            let mut new_full_item = full_item.clone();1242            new_full_item.owner = new_owner.clone();1243            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);12441245            // update balance1246            let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1247                .checked_add(value)1248                .unwrap();1249            <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);12501251            // update index collection1252            Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1253        } else {1254            let mut new_full_item = full_item.clone();1255            new_full_item.value -= val64;12561257            // separate amount1258            if new_owner_account_id > 0 {1259                // new owner has account1260                let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1261                item.value += val64;12621263                // update balance1264                let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1265                    .checked_add(value)1266                    .unwrap();1267                <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);12681269                <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1270            } else {1271                // new owner do not have account1272                let item = FungibleItemType {1273                    collection: collection_id,1274                    owner: new_owner.clone(),1275                    value: val64,1276                };12771278                Self::add_fungible_item(item)?;1279            }12801281            if amount == val64 {1282                Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;12831284                // remove approve list1285                <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1286                <FungibleItemList<T>>::remove(collection_id, item_id);1287            }12881289            <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1290        }12911292        Ok(())1293    }12941295    fn transfer_refungible(1296        collection_id: u64,1297        item_id: u64,1298        value: u64,1299        owner: T::AccountId,1300        new_owner: T::AccountId,1301    ) -> DispatchResult {13021303        ensure!(1304            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1305            "Item not exists"1306        );13071308        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1309        let item = full_item1310            .owner1311            .iter()1312            .filter(|i| i.owner == owner)1313            .next()1314            .unwrap();1315        let amount = item.fraction;13161317        ensure!(amount >= value.into(), "Item balance not enouth");13181319        // update balance1320        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1321            .checked_sub(value)1322            .unwrap();1323        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);13241325        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1326            .checked_add(value)1327            .unwrap();1328        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);13291330        let old_owner = item.owner.clone();1331        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1332        let val64 = value.into();13331334        // transfer1335        if amount == val64 && !new_owner_has_account {1336            // change owner1337            // new owner do not have account1338            let mut new_full_item = full_item.clone();1339            new_full_item1340                .owner1341                .iter_mut()1342                .find(|i| i.owner == owner)1343                .unwrap()1344                .owner = new_owner.clone();1345            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);13461347            // update index collection1348            Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1349        } else {1350            let mut new_full_item = full_item.clone();1351            new_full_item1352                .owner1353                .iter_mut()1354                .find(|i| i.owner == owner)1355                .unwrap()1356                .fraction -= val64;13571358            // separate amount1359            if new_owner_has_account {1360                // new owner has account1361                new_full_item1362                    .owner1363                    .iter_mut()1364                    .find(|i| i.owner == new_owner)1365                    .unwrap()1366                    .fraction += val64;1367            } else {1368                // new owner do not have account1369                new_full_item.owner.push(Ownership {1370                    owner: new_owner.clone(),1371                    fraction: val64,1372                });1373                Self::add_token_index(collection_id, item_id, new_owner.clone())?;1374            }13751376            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1377        }13781379        Ok(())1380    }13811382    fn transfer_nft(1383        collection_id: u64,1384        item_id: u64,1385        sender: T::AccountId,1386        new_owner: T::AccountId,1387    ) -> DispatchResult {1388    1389        ensure!(1390            <NftItemList<T>>::contains_key(collection_id, item_id),1391            "Item not exists"1392        );13931394        let mut item = <NftItemList<T>>::get(collection_id, item_id);13951396        ensure!(1397            sender == item.owner,1398            "sender parameter and item owner must be equal"1399        );14001401        // update balance1402        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1403            .checked_sub(1)1404            .unwrap();1405        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14061407        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1408            .checked_add(1)1409            .unwrap();1410        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14111412        // change owner1413        let old_owner = item.owner.clone();1414        item.owner = new_owner.clone();1415        <NftItemList<T>>::insert(collection_id, item_id, item);14161417        // update index collection1418        Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;14191420        // reset approved list1421        <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1422        Ok(())1423    }14241425    fn init_collection(item: &CollectionType<T::AccountId>){14261427                // check params1428                assert!(item.decimal_points <= 4, "decimal_points parameter must be lower than 4");1429                assert!(item.name.len() <= 64, "Collection name can not be longer than 63 char");1430                assert!(item.name.len() <= 256, "Collection description can not be longer than 255 char");1431                assert!(item.token_prefix.len() <= 16, "Token prefix can not be longer than 15 char");1432    1433                // Generate next collection ID1434                let next_id = CreatedCollectionCount::get()1435                    .checked_add(1)1436                    .expect("collection id error");1437    1438                CreatedCollectionCount::put(next_id);  1439    }14401441    fn init_nft_token(item: &NftItemType<T::AccountId>){14421443        let current_index = <ItemListIndex>::get(item.collection)1444            .checked_add(1)1445            .expect("Item list index id error");14461447        let item_owner = item.owner.clone();1448        let collection_id = item.collection.clone();1449        Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();14501451        <ItemListIndex>::insert(collection_id, current_index);14521453        // Update balance1454        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1455            .checked_add(1)1456            .unwrap();1457        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1458    }14591460    fn init_fungible_token(item: &FungibleItemType<T::AccountId>){14611462        let current_index = <ItemListIndex>::get(item.collection)1463            .checked_add(1)1464            .expect("Item list index id error");1465        let owner = item.owner.clone();1466        let value = item.value as u64;14671468        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();14691470        <ItemListIndex>::insert(item.collection, current_index);14711472        // Update balance1473        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1474            .checked_add(value)1475            .unwrap();1476        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1477    }14781479    fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>){14801481        let current_index = <ItemListIndex>::get(item.collection)1482            .checked_add(1)1483            .expect("Item list index id error");14841485        let value = item.owner.first().unwrap().fraction as u64;1486        let owner = item.owner.first().unwrap().owner.clone();14871488        Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();14891490        <ItemListIndex>::insert(item.collection, current_index);14911492        // Update balance1493        let new_balance = <Balance<T>>::get(item.collection, owner.clone())1494            .checked_add(value)1495            .unwrap();1496        <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1497    }14981499    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {1500        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1501        if list_exists {1502            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1503            let item_contains = list.contains(&item_index.clone());15041505            if !item_contains {1506                list.push(item_index.clone());1507            }15081509            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1510        } else {1511            let mut itm = Vec::new();1512            itm.push(item_index.clone());1513            <AddressTokens<T>>::insert(collection_id, owner, itm);1514        }15151516        Ok(())1517    }15181519    fn remove_token_index(1520        collection_id: u64,1521        item_index: u64,1522        owner: T::AccountId,1523    ) -> DispatchResult {1524        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1525        if list_exists {1526            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1527            let item_contains = list.contains(&item_index.clone());15281529            if item_contains {1530                list.retain(|&item| item != item_index);1531                <AddressTokens<T>>::insert(collection_id, owner, list);1532            }1533        }15341535        Ok(())1536    }15371538    fn move_token_index(1539        collection_id: u64,1540        item_index: u64,1541        old_owner: T::AccountId,1542        new_owner: T::AccountId,1543    ) -> DispatchResult {1544        Self::remove_token_index(collection_id, item_index, old_owner)?;1545        Self::add_token_index(collection_id, item_index, new_owner)?;15461547        Ok(())1548    }1549}15501551////////////////////////////////////////////////////////////////////////////////////////////////////1552// Economic models1553// #region15541555/// Fee multiplier.1556pub type Multiplier = FixedU128;15571558type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1559    <T as system::Trait>::AccountId,1560>>::Balance;1561type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1562    <T as system::Trait>::AccountId,1563>>::NegativeImbalance;15641565/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1566/// in the queue.1567#[derive(Encode, Decode, Clone, Eq, PartialEq)]1568pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1569    #[codec(compact)] BalanceOf<T>,1570);15711572impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1573    for ChargeTransactionPayment<T>1574{1575    #[cfg(feature = "std")]1576    fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1577        write!(f, "ChargeTransactionPayment<{:?}>", self.0)1578    }1579    #[cfg(not(feature = "std"))]1580    fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1581        Ok(())1582    }1583}15841585impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1586where1587    T::Call:1588        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1589    BalanceOf<T>: Send + Sync + FixedPointOperand,1590{1591    /// utility constructor. Used only in client/factory code.1592    pub fn from(fee: BalanceOf<T>) -> Self {1593        Self(fee)1594    }15951596    pub fn traditional_fee(1597        len: usize,1598        info: &DispatchInfoOf<T::Call>,1599        tip: BalanceOf<T>,1600    ) -> BalanceOf<T>1601    where1602        T::Call: Dispatchable<Info = DispatchInfo>,1603    {1604        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1605    }16061607    fn withdraw_fee(1608        &self,1609        who: &T::AccountId,1610        call: &T::Call,1611        info: &DispatchInfoOf<T::Call>,1612        len: usize,1613    ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1614        let tip = self.0;16151616        // Set fee based on call type. Creating collection costs 1 Unique.1617        // All other transactions have traditional fees so far1618        let fee = match call.is_sub_type() {1619            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1620            _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1621                                                        // _ => <BalanceOf<T>>::from(100)1622        };16231624        // Determine who is paying transaction fee based on ecnomic model1625        // Parse call to extract collection ID and access collection sponsor1626        let sponsor: T::AccountId = match call.is_sub_type() {1627            Some(Call::create_item(collection_id, _properties, _owner)) => {1628                <Collection<T>>::get(collection_id).sponsor1629            }1630            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1631                <Collection<T>>::get(collection_id).sponsor1632            }16331634            _ => T::AccountId::default(),1635        };16361637        let mut who_pays_fee: T::AccountId = sponsor.clone();1638        if sponsor == T::AccountId::default() {1639            who_pays_fee = who.clone();1640        }16411642        // Only mess with balances if fee is not zero.1643        if fee.is_zero() {1644            return Ok((fee, None));1645        }16461647        match <T as transaction_payment::Trait>::Currency::withdraw(1648            &who_pays_fee,1649            fee,1650            if tip.is_zero() {1651                WithdrawReason::TransactionPayment.into()1652            } else {1653                WithdrawReason::TransactionPayment | WithdrawReason::Tip1654            },1655            ExistenceRequirement::KeepAlive,1656        ) {1657            Ok(imbalance) => Ok((fee, Some(imbalance))),1658            Err(_) => Err(InvalidTransaction::Payment.into()),1659        }1660    }1661}16621663impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1664    for ChargeTransactionPayment<T>1665where1666    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1667    T::Call:1668        Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Module<T>, T>,1669{1670    const IDENTIFIER: &'static str = "ChargeTransactionPayment";1671    type AccountId = T::AccountId;1672    type Call = T::Call;1673    type AdditionalSigned = ();1674    type Pre = (1675        BalanceOf<T>,1676        Self::AccountId,1677        Option<NegativeImbalanceOf<T>>,1678        BalanceOf<T>,1679    );1680    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1681        Ok(())1682    }16831684    fn validate(1685        &self,1686        who: &Self::AccountId,1687        call: &Self::Call,1688        info: &DispatchInfoOf<Self::Call>,1689        len: usize,1690    ) -> TransactionValidity {1691        let (fee, _) = self.withdraw_fee(who, call, info, len)?;16921693        let mut r = ValidTransaction::default();1694        // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which1695        // will be a bit more than setting the priority to tip. For now, this is enough.1696        r.priority = fee.saturated_into::<TransactionPriority>();1697        Ok(r)1698    }16991700    fn pre_dispatch(1701        self,1702        who: &Self::AccountId,1703        call: &Self::Call,1704        info: &DispatchInfoOf<Self::Call>,1705        len: usize,1706    ) -> Result<Self::Pre, TransactionValidityError> {1707        let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1708        Ok((self.0, who.clone(), imbalance, fee))1709    }17101711    fn post_dispatch(1712        pre: Self::Pre,1713        info: &DispatchInfoOf<Self::Call>,1714        post_info: &PostDispatchInfoOf<Self::Call>,1715        len: usize,1716        _result: &DispatchResult,1717    ) -> Result<(), TransactionValidityError> {1718        let (tip, who, imbalance, fee) = pre;1719        if let Some(payed) = imbalance {1720            let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1721                len as u32, info, post_info, tip,1722            );1723            let refund = fee.saturating_sub(actual_fee);1724            let actual_payment =1725                match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1726                    &who, refund,1727                ) {1728                    Ok(refund_imbalance) => {1729                        // The refund cannot be larger than the up front payed max weight.1730                        // `PostDispatchInfo::calc_unspent` guards against such a case.1731                        match payed.offset(refund_imbalance) {1732                            Ok(actual_payment) => actual_payment,1733                            Err(_) => return Err(InvalidTransaction::Payment.into()),1734                        }1735                    }1736                    // We do not recreate the account using the refund. The up front payment1737                    // is gone in that case.1738                    Err(_) => payed,1739                };1740            let imbalances = actual_payment.split(tip);1741            <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1742                Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1743            );1744        }1745        Ok(())1746    }1747}1748// #endregion