git.delta.rocks / unique-network / refs/commits / 10c381b42680

difftreelog

Fix merge problems

Greg Zaitsev2020-07-30parent: #5b6d2a3.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/// For more guidance on Substrate FRAME, see the example pallet4/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs56use codec::{Decode, Encode};7pub use frame_support::{8    decl_event, decl_module, decl_storage,9    construct_runtime, parameter_types,10    traits::{Currency, Get, ExistenceRequirement, KeyOwnerProofSystem, OnUnbalanced, Randomness, WithdrawReason, Imbalance},11    weights::{12        DispatchInfo, PostDispatchInfo, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},13        IdentityFee, Weight, WeightToFeePolynomial, GetDispatchInfo, Pays,14    },15    StorageValue,16    dispatch::DispatchResult, 17    IsSubType,18    ensure19};2021use frame_system::{self as system, ensure_signed};22use sp_runtime::sp_std::prelude::Vec;23use sp_std::prelude::*;24use sp_runtime::{25	FixedU128, FixedPointOperand, 26	transaction_validity::{27		TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, TransactionValidity28	},29	traits::{30        Saturating, Dispatchable, DispatchInfoOf, PostDispatchInfoOf, SignedExtension, Zero, SaturatedConversion,31	},32};3334#[cfg(test)]35mod mock;3637#[cfg(test)]38mod tests;3940#[derive(Encode, Decode, Debug, Clone, PartialEq)]41pub enum AccessMode {42    Normal,43	WhiteList,44}45impl Default for AccessMode { fn default() -> Self { Self::Normal } }4647#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]48pub enum CollectionMode {49    Invalid,50	NFT,51	Fungible,52	ReFungible,53}54impl Default for CollectionMode { fn default() -> Self { Self::Invalid } }5556#[derive(Encode, Decode, Default, Clone, PartialEq)]57#[cfg_attr(feature = "std", derive(Debug))]58pub struct Ownership<AccountId> {59    pub owner: AccountId,60    pub fraction: u12861}6263#[derive(Encode, Decode, Default, Clone, PartialEq)]64#[cfg_attr(feature = "std", derive(Debug))]65pub struct CollectionType<AccountId> {66    pub owner: AccountId,67    pub mode: CollectionMode,68    pub access: AccessMode,69    pub next_item_id: u64,70    pub decimal_points: u32,71    pub name: Vec<u16>,        // 64 include null escape char72    pub description: Vec<u16>, // 256 include null escape char73    pub token_prefix: Vec<u8>, // 16 include null escape char74    pub custom_data_size: u32,75    pub sponsor: AccountId,    // Who pays fees. If set to default address, the fees are applied to the transaction sender76    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship77}7879#[derive(Encode, Decode, Default, Clone, PartialEq)]80#[cfg_attr(feature = "std", derive(Debug))]81pub struct CollectionAdminsType<AccountId> {82    pub admin: AccountId,83    pub collection_id: u64,84}8586#[derive(Encode, Decode, Default, Clone, PartialEq)]87#[cfg_attr(feature = "std", derive(Debug))]88pub struct NftItemType<AccountId> {89    pub collection: u64,90    pub owner: AccountId,91    pub data: Vec<u8>,92}9394#[derive(Encode, Decode, Default, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Debug))]96pub struct FungibleItemType<AccountId> {97    pub collection: u64,98    pub owner: Vec<AccountId>,99    pub data: Vec<u64>,100}101102#[derive(Encode, Decode, Default, Clone, PartialEq)]103#[cfg_attr(feature = "std", derive(Debug))]104pub struct ReFungibleItemType<AccountId> {105    pub collection: u64,106    pub owner: Vec<Ownership<AccountId>>,107}108109pub trait Trait: system::Trait {110    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;111112}113114decl_storage! {115    trait Store for Module<T: Trait> as Nft {116117        // Next available collection ID118        NextCollectionID get(fn next_collection_id): u64;119        pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;120        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;121122        // Balance owner per collection map123        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;124        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<T::AccountId>;125126        // Item collections127        pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;128        pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;129        pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;130        ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64;131132        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;133    }134}135136decl_event!(137    pub enum Event<T>138    where139        AccountId = <T as system::Trait>::AccountId,140    {141        Created(u64, u8, AccountId),142        ItemCreated(u64, u64),143        ItemDestroyed(u64, u64),144    }145);146147decl_module! {148    pub struct Module<T: Trait> for enum Call where origin: T::Origin {149150        fn deposit_event() = default;151152        // Create collection of NFT with given parameters153        //154        // @param customDataSz size of custom data in each collection item155        // returns collection ID156        #[weight = 0]157        pub fn create_collection(   origin,158                                    collection_name: Vec<u16>,159                                    collection_description: Vec<u16>,160                                    token_prefix: Vec<u8>,161                                    mode: u8,162                                    decimal_points: u32,163                                    custom_data_size: u32) -> DispatchResult {164165            // Anyone can create a collection166            let who = ensure_signed(origin)?;167            let collection_mode: CollectionMode;168            match mode {169                1 => collection_mode = CollectionMode::NFT,170                2 => collection_mode = CollectionMode::Fungible,171                3 => collection_mode = CollectionMode::ReFungible,172                _ => collection_mode = CollectionMode::Invalid173            }174175            // check type176            ensure!((collection_mode == CollectionMode::Fungible || collection_mode == CollectionMode::NFT || collection_mode == CollectionMode::ReFungible), 177                "Collection mode must be Fungible, NFT or ReFungible");          178179            // NFT checks180            if collection_mode == CollectionMode::NFT181            {182                ensure!(decimal_points == 0, "Collection in NFT mode must have zero in decimal_points parameter"); 183            }184185            // Fungible checks186            if collection_mode == CollectionMode::Fungible187            {188                ensure!(custom_data_size == 0, "Collection in Fungible mode must have zero in custom_data_size parameter"); 189                ensure!(decimal_points != 0, "Collection in Fungible mode must have not zero in decimal_points parameter"); 190            }191192            // check params193            ensure!(decimal_points < 100, "decimal_points parameter must be lower than 100"); 194195            let mut name = collection_name.to_vec();196            name.push(0);197            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");198199            let mut description = collection_description.to_vec();200            description.push(0);201            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");202203            let mut prefix = token_prefix.to_vec();204            prefix.push(0);205            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");206207            // Generate next collection ID208            let next_id = NextCollectionID::get()209                .checked_add(1)210                .expect("collection id error");211212            NextCollectionID::put(next_id);213214            // Create new collection215            let new_collection = CollectionType {216                owner: who.clone(),217                name: name,218                mode: collection_mode.clone(),219                access: AccessMode::Normal,220                description: description,221                decimal_points: decimal_points,222                token_prefix: prefix,223                next_item_id: next_id,224                custom_data_size: custom_data_sz,225                sponsor: T::AccountId::default(),226                unconfirmed_sponsor: T::AccountId::default(),227            };228229            // Add new collection to map230            <Collection<T>>::insert(next_id, new_collection);231232            // call event233            Self::deposit_event(RawEvent::Created(next_id, mode, who.clone()));234235            Ok(())236        }237238        #[weight = 0]239        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {240241            let sender = ensure_signed(origin)?;242            Self::check_owner_permissions(collection_id, sender)?;243244            <AddressTokens<T>>::remove_prefix(collection_id);245            <ApprovedList<T>>::remove_prefix(collection_id);246            <Balance<T>>::remove_prefix(collection_id);247            <ItemListIndex>::remove(collection_id);248            <AdminList<T>>::remove(collection_id);249            <Collection<T>>::remove(collection_id);250251            Ok(())252        }253254        #[weight = 0]255        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {256257            let sender = ensure_signed(origin)?;258            Self::check_owner_permissions(collection_id, sender)?;259            let mut target_collection = <Collection<T>>::get(collection_id);260            target_collection.owner = new_owner;261            <Collection<T>>::insert(collection_id, target_collection);262263            Ok(())264        }265266        #[weight = 0]267        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {268269            let sender = ensure_signed(origin)?;270            Self::check_owner_or_admin_permissions(collection_id, sender)?;271            let mut admin_arr: Vec<T::AccountId> = Vec::new();272273            if <AdminList<T>>::contains_key(collection_id)274            {275                admin_arr = <AdminList<T>>::get(collection_id);276                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");277            }278279            admin_arr.push(new_admin_id);280            <AdminList<T>>::insert(collection_id, admin_arr);281282            Ok(())283        }284285        #[weight = 0]286        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {287288            let sender = ensure_signed(origin)?;289            Self::check_owner_or_admin_permissions(collection_id, sender)?;290291            if <AdminList<T>>::contains_key(collection_id)292            {293                let mut admin_arr = <AdminList<T>>::get(collection_id);294                admin_arr.retain(|i| *i != account_id);295                <AdminList<T>>::insert(collection_id, admin_arr);296            }297298            Ok(())299        }300301        #[weight = 0]302        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {303304            let sender = ensure_signed(origin)?;305            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");306307            let mut target_collection = <Collection<T>>::get(collection_id);308            ensure!(sender == target_collection.owner, "You do not own this collection");309310            target_collection.unconfirmed_sponsor = new_sponsor;311            <Collection<T>>::insert(collection_id, target_collection);312313            Ok(())314        }315316        #[weight = 0]317        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {318319            let sender = ensure_signed(origin)?;320            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");321322            let mut target_collection = <Collection<T>>::get(collection_id);323            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");324325            target_collection.sponsor = target_collection.unconfirmed_sponsor;326            target_collection.unconfirmed_sponsor = T::AccountId::default();327            <Collection<T>>::insert(collection_id, target_collection);328329            Ok(())330        }331332        #[weight = 0]333        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {334335            let sender = ensure_signed(origin)?;336            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");337338            let mut target_collection = <Collection<T>>::get(collection_id);339            ensure!(sender == target_collection.owner, "You do not own this collection");340341            target_collection.sponsor = T::AccountId::default();342            <Collection<T>>::insert(collection_id, target_collection);343344            Ok(())345        }346        347348349        #[weight = 0]350        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>) -> DispatchResult {351352            let sender = ensure_signed(origin)?;353354            // check size355            let target_collection = <Collection<T>>::get(collection_id);356            ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");357358            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;359360            let new_balance = <Balance<T>>::get(collection_id, owner.clone()) + 1;361            <Balance<T>>::insert(collection_id, owner.clone(), new_balance);362363            // TODO: implement other modes364            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");365366            if target_collection.mode == CollectionMode::NFT367            {368                // Create nft item369                let item = NftItemType {370                    collection: collection_id,371                    owner: owner,372                    data: properties,373                };374375                Self::add_nft_item(item)?;376            }377378            // call event379            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));380381            Ok(())382        }383384        #[weight = 0]385        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {386387            let sender = ensure_signed(origin)?;388            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);389            if !item_owner390            {391                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;392            }393            394            Self::burn_nft_item(collection_id, item_id)?;395396            // call event397            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));398399            Ok(())400        }401402        #[weight = 0]403        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {404405            let sender = ensure_signed(origin)?;406            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);407            if !item_owner408            {409                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;410            }411412            let target_collection = <Collection<T>>::get(collection_id);413414            // TODO: implement other modes415            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");416417            if target_collection.mode == CollectionMode::NFT418            {419                Self::transfer_nft(collection_id, item_id, recipient)?;420            }421422            Ok(())423        }424425        #[weight = 0]426        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {427428            let sender = ensure_signed(origin)?;429430            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);431            if !item_owner432            {433                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;434            }435436            let list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);437            if list_exists {438439                let mut list = <ApprovedList<T>>::get(collection_id, item_id);440                let item_contains = list.contains(&approved.clone());441442                if !item_contains {443                    list.push(approved.clone());444                }445            } else {446447                let mut itm = Vec::new();448                itm.push(approved.clone());449                <ApprovedList<T>>::insert(collection_id, item_id, itm);450            }451452            Ok(())453        }454455        #[weight = 0]456        pub fn transfer_from(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, ) -> DispatchResult {457458            let mut approved: bool = false; 459            let sender = ensure_signed(origin)?;460            let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);461            if approved_list_exists462            {463                let list_itm = <ApprovedList<T>>::get(collection_id, item_id);464                approved = list_itm.contains(&recipient.clone());465            }466467            if !approved468            {469                Self::check_owner_or_admin_permissions(collection_id, sender)?;470            }471            472            let target_collection = <Collection<T>>::get(collection_id);473474            // TODO: implement other modes475            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");476477            if target_collection.mode == CollectionMode::NFT478            {479                Self::transfer_nft(collection_id, item_id, recipient)?;480            }481482            Ok(())483        }484485        #[weight = 0]486        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {487488            // let no_perm_mes = "You do not have permissions to modify this collection";489            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);490            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));491            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);492493            // // on_nft_received  call494495            // Self::transfer(origin, collection_id, item_id, new_owner)?;496497            Ok(())498        }499    }500}501502impl<T: Trait> Module<T> {503504    fn collection_exists(collection_id: u64) -> DispatchResult{505        ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");506        Ok(())507    }508509    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {510511        Self::collection_exists(collection_id)?;512513        let target_collection = <Collection<T>>::get(collection_id);514        ensure!(subject == target_collection.owner, "You do not own this collection");515516        Ok(())517    }518519    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {520521        Self::collection_exists(collection_id)?;522523        let target_collection = <Collection<T>>::get(collection_id);524        let is_owner = subject == target_collection.owner;525526        let no_perm_mes = "You do not have permissions to modify this collection";527        let exists = <AdminList<T>>::contains_key(collection_id);528529        if !is_owner530        {531            ensure!(exists, no_perm_mes);532            ensure!(<AdminList<T>>::get(collection_id).contains(&subject), no_perm_mes);533        }534        Ok(())535    }536537    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool{538539        let mut result = false;540        let target_collection = <Collection<T>>::get(collection_id);541542        if target_collection.mode == CollectionMode::NFT543        {544            let item = <NftItemList<T>>::get(collection_id, item_id);545            result = item.owner == subject;546        }547548        if target_collection.mode == CollectionMode::Fungible549        {550            let item = <FungibleItemList<T>>::get(collection_id, item_id);551            result = item.owner.contains(&subject);552        }553554        if target_collection.mode == CollectionMode::ReFungible555        {556            let item = <ReFungibleItemList<T>>::get(collection_id, item_id);557            result = item.owner.iter().any(|i| i.owner == subject);558        }559560        result561    }562563    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {564565        let current_index = <ItemListIndex>::get(item.collection)566        .checked_add(1)567        .expect("Item list index id error");568569        Self::add_token_index(item.collection, current_index, item.owner.clone())?;570571        <ItemListIndex>::insert(item.collection, current_index);572        <NftItemList<T>>::insert(item.collection, current_index, item);573574        Ok(())575    }576577    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {578  579        let item = <NftItemList<T>>::get(collection_id, item_id);580        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;581582        // update balance583        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();584        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);585        <NftItemList<T>>::remove(collection_id, item_id);586587        Ok(())588    }589590    fn transfer_nft(collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {591592        let mut item = <NftItemList<T>>::get(collection_id, item_id);593594        // update balance595        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();596        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);597598        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone()).checked_add(1).unwrap();599        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);600601        // change owner602        let old_owner = item.owner.clone();603        item.owner = new_owner.clone();604        <NftItemList<T>>::insert(collection_id, item_id, item);605606        // update index collection607        Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?;608609        // reset approved list610        let itm: Vec<T::AccountId> = Vec::new();611        <ApprovedList<T>>::insert(collection_id, item_id, itm);612613        Ok(())614    }615616    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {617        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());618        if list_exists {619            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());620            let item_contains = list.contains(&item_index.clone());621622            if !item_contains {623                list.push(item_index.clone());624            }625626            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);627        } else {628            let mut itm = Vec::new();629            itm.push(item_index.clone());630            <AddressTokens<T>>::insert(collection_id, owner, itm);631        }632633        Ok(())634    }635636    fn remove_token_index(637        collection_id: u64,638        item_index: u64,639        owner: T::AccountId,640    ) -> DispatchResult {641        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());642        if list_exists {643            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());644            let item_contains = list.contains(&item_index.clone());645646            if item_contains {647                list.retain(|&item| item != item_index);648                <AddressTokens<T>>::insert(collection_id, owner, list);649            }650        }651652        Ok(())653    }654655    fn move_token_index(656        collection_id: u64,657        item_index: u64,658        old_owner: T::AccountId,659        new_owner: T::AccountId,660    ) -> DispatchResult {661        Self::remove_token_index(collection_id, item_index, old_owner)?;662        Self::add_token_index(collection_id, item_index, new_owner)?;663664        Ok(())665    }666}667668669////////////////////////////////////////////////////////////////////////////////////////////////////670// Economic models671672/// Fee multiplier.673pub type Multiplier = FixedU128;674675type BalanceOf<T> =676	<<T as transaction_payment::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;677type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<678	<T as system::Trait>::AccountId,>>::NegativeImbalance;679680681682/// Require the transactor pay for themselves and maybe include a tip to gain additional priority683/// in the queue.684#[derive(Encode, Decode, Clone, Eq, PartialEq)]685pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(#[codec(compact)] BalanceOf<T>);686687impl<T:Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug for ChargeTransactionPayment<T> {688	#[cfg(feature = "std")]689	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {690		write!(f, "ChargeTransactionPayment<{:?}>", self.0)691	}692	#[cfg(not(feature = "std"))]693	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {694		Ok(())695	}696}697698impl<T:Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T> where699	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,700	BalanceOf<T>: Send + Sync + FixedPointOperand,701{702	/// utility constructor. Used only in client/factory code.703	pub fn from(fee: BalanceOf<T>) -> Self {704		Self(fee)705	}706707    pub fn traditional_fee(708        len: usize,709        info: &DispatchInfoOf<T::Call>,710        tip: BalanceOf<T>,711    ) -> BalanceOf<T> where712        T::Call: Dispatchable<Info=DispatchInfo>,713    {714        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)715    }716717	fn withdraw_fee(718		&self,719        who: &T::AccountId,720        call: &T::Call,721		info: &DispatchInfoOf<T::Call>,722		len: usize,723	) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {724        let tip = self.0;725726        // Set fee based on call type. Creating collection costs 1 Unique.727        // All other transactions have traditional fees so far728        let fee = match call.is_sub_type() {729            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),730            _ => Self::traditional_fee(len, info, tip)731732            // Flat fee model, use only for testing purposes733            // _ => <BalanceOf<T>>::from(100)734        };735736        // Determine who is paying transaction fee based on ecnomic model737        // Parse call to extract collection ID and access collection sponsor738        let sponsor: T::AccountId = match call.is_sub_type() {739            Some(Call::create_item(collection_id, _properties)) => {740                <Collection<T>>::get(collection_id).sponsor741            },742            Some(Call::transfer(collection_id, _item_id, _new_owner)) => {743                <Collection<T>>::get(collection_id).sponsor744            },745746            _ => T::AccountId::default()747        };748749        let mut who_pays_fee: T::AccountId = sponsor.clone();750        if sponsor == T::AccountId::default() {751            who_pays_fee = who.clone();752        }753754		// Only mess with balances if fee is not zero.755		if fee.is_zero() {756			return Ok((fee, None));757		}758759		match <T as transaction_payment::Trait>::Currency::withdraw(760			&who_pays_fee,761			fee,762			if tip.is_zero() {763				WithdrawReason::TransactionPayment.into()764			} else {765				WithdrawReason::TransactionPayment | WithdrawReason::Tip766			},767			ExistenceRequirement::KeepAlive,768		) {769			Ok(imbalance) => Ok((fee, Some(imbalance))),770			Err(_) => Err(InvalidTransaction::Payment.into()),771		}772	}773}774775impl<T:Trait + transaction_payment::Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> where776    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,777    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,778{779	const IDENTIFIER: &'static str = "ChargeTransactionPayment";780	type AccountId = T::AccountId;781	type Call = T::Call;782	type AdditionalSigned = ();783	type Pre = (BalanceOf<T>, Self::AccountId, Option<NegativeImbalanceOf<T>>, BalanceOf<T>);784	fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }785786	fn validate(787		&self,788		who: &Self::AccountId,789		call: &Self::Call,790		info: &DispatchInfoOf<Self::Call>,791		len: usize,792	) -> TransactionValidity {793		let (fee, _) = self.withdraw_fee(who, call, info, len)?;794795		let mut r = ValidTransaction::default();796		// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which797		// will be a bit more than setting the priority to tip. For now, this is enough.798		r.priority = fee.saturated_into::<TransactionPriority>();799		Ok(r)800	}801802	fn pre_dispatch(803		self,804		who: &Self::AccountId,805		call: &Self::Call,806		info: &DispatchInfoOf<Self::Call>,807		len: usize808	) -> Result<Self::Pre, TransactionValidityError> {809		let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;810		Ok((self.0, who.clone(), imbalance, fee))811	}812813	fn post_dispatch(814		pre: Self::Pre,815		info: &DispatchInfoOf<Self::Call>,816		post_info: &PostDispatchInfoOf<Self::Call>,817		len: usize,818		_result: &DispatchResult,819	) -> Result<(), TransactionValidityError> {820		let (tip, who, imbalance, fee) = pre;821		if let Some(payed) = imbalance {822			let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(823				len as u32,824				info,825				post_info,826				tip,827			);828			let refund = fee.saturating_sub(actual_fee);829			let actual_payment = match <T as transaction_payment::Trait>::Currency::deposit_into_existing(&who, refund) {830				Ok(refund_imbalance) => {831					// The refund cannot be larger than the up front payed max weight.832					// `PostDispatchInfo::calc_unspent` guards against such a case.833					match payed.offset(refund_imbalance) {834						Ok(actual_payment) => actual_payment,835						Err(_) => return Err(InvalidTransaction::Payment.into()),836					}837				}838				// We do not recreate the account using the refund. The up front payment839				// is gone in that case.840				Err(_) => payed,841			};842			let imbalances = actual_payment.split(tip);843			<T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(Some(imbalances.0).into_iter()844				.chain(Some(imbalances.1)));845		}846		Ok(())847	}848}
after · pallets/nft/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23/// For more guidance on Substrate FRAME, see the example pallet4/// https://github.com/paritytech/substrate/blob/master/frame/example/src/lib.rs56use codec::{Decode, Encode};7pub use frame_support::{8    decl_event, decl_module, decl_storage,9    construct_runtime, parameter_types,10    traits::{Currency, Get, ExistenceRequirement, KeyOwnerProofSystem, OnUnbalanced, Randomness, WithdrawReason, Imbalance},11    weights::{12        DispatchInfo, PostDispatchInfo, constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},13        IdentityFee, Weight, WeightToFeePolynomial, GetDispatchInfo, Pays,14    },15    StorageValue,16    dispatch::DispatchResult, 17    IsSubType,18    ensure19};2021use frame_system::{self as system, ensure_signed};22use sp_runtime::sp_std::prelude::Vec;23use sp_std::prelude::*;24use sp_runtime::{25	FixedU128, FixedPointOperand, 26	transaction_validity::{27		TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, TransactionValidity28	},29	traits::{30        Saturating, Dispatchable, DispatchInfoOf, PostDispatchInfoOf, SignedExtension, Zero, SaturatedConversion,31	},32};3334#[cfg(test)]35mod mock;3637#[cfg(test)]38mod tests;3940#[derive(Encode, Decode, Debug, Clone, PartialEq)]41pub enum AccessMode {42    Normal,43	WhiteList,44}45impl Default for AccessMode { fn default() -> Self { Self::Normal } }4647#[derive(Encode, Decode, Debug, Eq, Clone, PartialEq)]48pub enum CollectionMode {49    Invalid,50	NFT,51	Fungible,52	ReFungible,53}54impl Default for CollectionMode { fn default() -> Self { Self::Invalid } }5556#[derive(Encode, Decode, Default, Clone, PartialEq)]57#[cfg_attr(feature = "std", derive(Debug))]58pub struct Ownership<AccountId> {59    pub owner: AccountId,60    pub fraction: u12861}6263#[derive(Encode, Decode, Default, Clone, PartialEq)]64#[cfg_attr(feature = "std", derive(Debug))]65pub struct CollectionType<AccountId> {66    pub owner: AccountId,67    pub mode: CollectionMode,68    pub access: AccessMode,69    pub next_item_id: u64,70    pub decimal_points: u32,71    pub name: Vec<u16>,        // 64 include null escape char72    pub description: Vec<u16>, // 256 include null escape char73    pub token_prefix: Vec<u8>, // 16 include null escape char74    pub custom_data_size: u32,75    pub sponsor: AccountId,    // Who pays fees. If set to default address, the fees are applied to the transaction sender76    pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship77}7879#[derive(Encode, Decode, Default, Clone, PartialEq)]80#[cfg_attr(feature = "std", derive(Debug))]81pub struct CollectionAdminsType<AccountId> {82    pub admin: AccountId,83    pub collection_id: u64,84}8586#[derive(Encode, Decode, Default, Clone, PartialEq)]87#[cfg_attr(feature = "std", derive(Debug))]88pub struct NftItemType<AccountId> {89    pub collection: u64,90    pub owner: AccountId,91    pub data: Vec<u8>,92}9394#[derive(Encode, Decode, Default, Clone, PartialEq)]95#[cfg_attr(feature = "std", derive(Debug))]96pub struct FungibleItemType<AccountId> {97    pub collection: u64,98    pub owner: Vec<AccountId>,99    pub data: Vec<u64>,100}101102#[derive(Encode, Decode, Default, Clone, PartialEq)]103#[cfg_attr(feature = "std", derive(Debug))]104pub struct ReFungibleItemType<AccountId> {105    pub collection: u64,106    pub owner: Vec<Ownership<AccountId>>,107}108109pub trait Trait: system::Trait {110    type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;111112}113114decl_storage! {115    trait Store for Module<T: Trait> as Nft {116117        // Next available collection ID118        NextCollectionID get(fn next_collection_id): u64;119        pub Collection get(fn collection): map hasher(identity) u64 => CollectionType<T::AccountId>;120        pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;121122        // Balance owner per collection map123        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;124        pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<T::AccountId>;125126        // Item collections127        pub NftItemList get(fn nft_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;128        pub FungibleItemList get(fn fungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;129        pub ReFungibleItemList get(fn refungible_item_id): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;130        ItemListIndex get(fn item_index): map hasher(blake2_128_concat) u64 => u64;131132        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;133    }134}135136decl_event!(137    pub enum Event<T>138    where139        AccountId = <T as system::Trait>::AccountId,140    {141        Created(u64, u8, AccountId),142        ItemCreated(u64, u64),143        ItemDestroyed(u64, u64),144    }145);146147decl_module! {148    pub struct Module<T: Trait> for enum Call where origin: T::Origin {149150        fn deposit_event() = default;151152        // Create collection of NFT with given parameters153        //154        // @param customDataSz size of custom data in each collection item155        // returns collection ID156        #[weight = 0]157        pub fn create_collection(   origin,158                                    collection_name: Vec<u16>,159                                    collection_description: Vec<u16>,160                                    token_prefix: Vec<u8>,161                                    mode: u8,162                                    decimal_points: u32,163                                    custom_data_size: u32) -> DispatchResult {164165            // Anyone can create a collection166            let who = ensure_signed(origin)?;167            let collection_mode: CollectionMode;168            match mode {169                1 => collection_mode = CollectionMode::NFT,170                2 => collection_mode = CollectionMode::Fungible,171                3 => collection_mode = CollectionMode::ReFungible,172                _ => collection_mode = CollectionMode::Invalid173            }174175            // check type176            ensure!((collection_mode == CollectionMode::Fungible || collection_mode == CollectionMode::NFT || collection_mode == CollectionMode::ReFungible), 177                "Collection mode must be Fungible, NFT or ReFungible");          178179            // NFT checks180            if collection_mode == CollectionMode::NFT181            {182                ensure!(decimal_points == 0, "Collection in NFT mode must have zero in decimal_points parameter"); 183            }184185            // Fungible checks186            if collection_mode == CollectionMode::Fungible187            {188                ensure!(custom_data_size == 0, "Collection in Fungible mode must have zero in custom_data_size parameter"); 189                ensure!(decimal_points != 0, "Collection in Fungible mode must have not zero in decimal_points parameter"); 190            }191192            // check params193            ensure!(decimal_points < 100, "decimal_points parameter must be lower than 100"); 194195            let mut name = collection_name.to_vec();196            name.push(0);197            ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");198199            let mut description = collection_description.to_vec();200            description.push(0);201            ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");202203            let mut prefix = token_prefix.to_vec();204            prefix.push(0);205            ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");206207            // Generate next collection ID208            let next_id = NextCollectionID::get()209                .checked_add(1)210                .expect("collection id error");211212            NextCollectionID::put(next_id);213214            // Create new collection215            let new_collection = CollectionType {216                owner: who.clone(),217                name: name,218                mode: collection_mode.clone(),219                access: AccessMode::Normal,220                description: description,221                decimal_points: decimal_points,222                token_prefix: prefix,223                next_item_id: next_id,224                custom_data_size: custom_data_size,225                sponsor: T::AccountId::default(),226                unconfirmed_sponsor: T::AccountId::default(),227            };228229            // Add new collection to map230            <Collection<T>>::insert(next_id, new_collection);231232            // call event233            Self::deposit_event(RawEvent::Created(next_id, mode, who.clone()));234235            Ok(())236        }237238        #[weight = 0]239        pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {240241            let sender = ensure_signed(origin)?;242            Self::check_owner_permissions(collection_id, sender)?;243244            <AddressTokens<T>>::remove_prefix(collection_id);245            <ApprovedList<T>>::remove_prefix(collection_id);246            <Balance<T>>::remove_prefix(collection_id);247            <ItemListIndex>::remove(collection_id);248            <AdminList<T>>::remove(collection_id);249            <Collection<T>>::remove(collection_id);250251            Ok(())252        }253254        #[weight = 0]255        pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {256257            let sender = ensure_signed(origin)?;258            Self::check_owner_permissions(collection_id, sender)?;259            let mut target_collection = <Collection<T>>::get(collection_id);260            target_collection.owner = new_owner;261            <Collection<T>>::insert(collection_id, target_collection);262263            Ok(())264        }265266        #[weight = 0]267        pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {268269            let sender = ensure_signed(origin)?;270            Self::check_owner_or_admin_permissions(collection_id, sender)?;271            let mut admin_arr: Vec<T::AccountId> = Vec::new();272273            if <AdminList<T>>::contains_key(collection_id)274            {275                admin_arr = <AdminList<T>>::get(collection_id);276                ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");277            }278279            admin_arr.push(new_admin_id);280            <AdminList<T>>::insert(collection_id, admin_arr);281282            Ok(())283        }284285        #[weight = 0]286        pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {287288            let sender = ensure_signed(origin)?;289            Self::check_owner_or_admin_permissions(collection_id, sender)?;290291            if <AdminList<T>>::contains_key(collection_id)292            {293                let mut admin_arr = <AdminList<T>>::get(collection_id);294                admin_arr.retain(|i| *i != account_id);295                <AdminList<T>>::insert(collection_id, admin_arr);296            }297298            Ok(())299        }300301        #[weight = 0]302        pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {303304            let sender = ensure_signed(origin)?;305            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");306307            let mut target_collection = <Collection<T>>::get(collection_id);308            ensure!(sender == target_collection.owner, "You do not own this collection");309310            target_collection.unconfirmed_sponsor = new_sponsor;311            <Collection<T>>::insert(collection_id, target_collection);312313            Ok(())314        }315316        #[weight = 0]317        pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {318319            let sender = ensure_signed(origin)?;320            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");321322            let mut target_collection = <Collection<T>>::get(collection_id);323            ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");324325            target_collection.sponsor = target_collection.unconfirmed_sponsor;326            target_collection.unconfirmed_sponsor = T::AccountId::default();327            <Collection<T>>::insert(collection_id, target_collection);328329            Ok(())330        }331332        #[weight = 0]333        pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {334335            let sender = ensure_signed(origin)?;336            ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");337338            let mut target_collection = <Collection<T>>::get(collection_id);339            ensure!(sender == target_collection.owner, "You do not own this collection");340341            target_collection.sponsor = T::AccountId::default();342            <Collection<T>>::insert(collection_id, target_collection);343344            Ok(())345        }346        347        #[weight = 0]348        pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {349350            let sender = ensure_signed(origin)?;351352            // check size353            let target_collection = <Collection<T>>::get(collection_id);354            ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");355356            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;357358            let new_balance = <Balance<T>>::get(collection_id, owner.clone()) + 1;359            <Balance<T>>::insert(collection_id, owner.clone(), new_balance);360361            // TODO: implement other modes362            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");363364            if target_collection.mode == CollectionMode::NFT365            {366                // Create nft item367                let item = NftItemType {368                    collection: collection_id,369                    owner: owner,370                    data: properties,371                };372373                Self::add_nft_item(item)?;374            }375376            // call event377            Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));378379            Ok(())380        }381382        #[weight = 0]383        pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {384385            let sender = ensure_signed(origin)?;386            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);387            if !item_owner388            {389                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;390            }391            392            Self::burn_nft_item(collection_id, item_id)?;393394            // call event395            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));396397            Ok(())398        }399400        #[weight = 0]401        pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {402403            let sender = ensure_signed(origin)?;404            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);405            if !item_owner406            {407                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;408            }409410            let target_collection = <Collection<T>>::get(collection_id);411412            // TODO: implement other modes413            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");414415            if target_collection.mode == CollectionMode::NFT416            {417                Self::transfer_nft(collection_id, item_id, recipient)?;418            }419420            Ok(())421        }422423        #[weight = 0]424        pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {425426            let sender = ensure_signed(origin)?;427428            let item_owner = Self::is_item_owner(sender.clone(), collection_id, item_id);429            if !item_owner430            {431                Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;432            }433434            let list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);435            if list_exists {436437                let mut list = <ApprovedList<T>>::get(collection_id, item_id);438                let item_contains = list.contains(&approved.clone());439440                if !item_contains {441                    list.push(approved.clone());442                }443            } else {444445                let mut itm = Vec::new();446                itm.push(approved.clone());447                <ApprovedList<T>>::insert(collection_id, item_id, itm);448            }449450            Ok(())451        }452453        #[weight = 0]454        pub fn transfer_from(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, ) -> DispatchResult {455456            let mut approved: bool = false; 457            let sender = ensure_signed(origin)?;458            let approved_list_exists = <ApprovedList<T>>::contains_key(collection_id, item_id);459            if approved_list_exists460            {461                let list_itm = <ApprovedList<T>>::get(collection_id, item_id);462                approved = list_itm.contains(&recipient.clone());463            }464465            if !approved466            {467                Self::check_owner_or_admin_permissions(collection_id, sender)?;468            }469            470            let target_collection = <Collection<T>>::get(collection_id);471472            // TODO: implement other modes473            ensure!(target_collection.mode == CollectionMode::NFT, "Collection type not implemented");474475            if target_collection.mode == CollectionMode::NFT476            {477                Self::transfer_nft(collection_id, item_id, recipient)?;478            }479480            Ok(())481        }482483        #[weight = 0]484        pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {485486            // let no_perm_mes = "You do not have permissions to modify this collection";487            // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);488            // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));489            // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);490491            // // on_nft_received  call492493            // Self::transfer(origin, collection_id, item_id, new_owner)?;494495            Ok(())496        }497    }498}499500impl<T: Trait> Module<T> {501502    fn collection_exists(collection_id: u64) -> DispatchResult{503        ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");504        Ok(())505    }506507    fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {508509        Self::collection_exists(collection_id)?;510511        let target_collection = <Collection<T>>::get(collection_id);512        ensure!(subject == target_collection.owner, "You do not own this collection");513514        Ok(())515    }516517    fn check_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {518519        Self::collection_exists(collection_id)?;520521        let target_collection = <Collection<T>>::get(collection_id);522        let is_owner = subject == target_collection.owner;523524        let no_perm_mes = "You do not have permissions to modify this collection";525        let exists = <AdminList<T>>::contains_key(collection_id);526527        if !is_owner528        {529            ensure!(exists, no_perm_mes);530            ensure!(<AdminList<T>>::get(collection_id).contains(&subject), no_perm_mes);531        }532        Ok(())533    }534535    fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool{536537        let mut result = false;538        let target_collection = <Collection<T>>::get(collection_id);539540        if target_collection.mode == CollectionMode::NFT541        {542            let item = <NftItemList<T>>::get(collection_id, item_id);543            result = item.owner == subject;544        }545546        if target_collection.mode == CollectionMode::Fungible547        {548            let item = <FungibleItemList<T>>::get(collection_id, item_id);549            result = item.owner.contains(&subject);550        }551552        if target_collection.mode == CollectionMode::ReFungible553        {554            let item = <ReFungibleItemList<T>>::get(collection_id, item_id);555            result = item.owner.iter().any(|i| i.owner == subject);556        }557558        result559    }560561    fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {562563        let current_index = <ItemListIndex>::get(item.collection)564        .checked_add(1)565        .expect("Item list index id error");566567        Self::add_token_index(item.collection, current_index, item.owner.clone())?;568569        <ItemListIndex>::insert(item.collection, current_index);570        <NftItemList<T>>::insert(item.collection, current_index, item);571572        Ok(())573    }574575    fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {576  577        let item = <NftItemList<T>>::get(collection_id, item_id);578        Self::remove_token_index(collection_id, item_id, item.owner.clone())?;579580        // update balance581        let new_balance = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();582        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);583        <NftItemList<T>>::remove(collection_id, item_id);584585        Ok(())586    }587588    fn transfer_nft(collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {589590        let mut item = <NftItemList<T>>::get(collection_id, item_id);591592        // update balance593        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone()).checked_sub(1).unwrap();594        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);595596        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone()).checked_add(1).unwrap();597        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);598599        // change owner600        let old_owner = item.owner.clone();601        item.owner = new_owner.clone();602        <NftItemList<T>>::insert(collection_id, item_id, item);603604        // update index collection605        Self::move_token_index(collection_id, item_id, old_owner, new_owner.clone())?;606607        // reset approved list608        let itm: Vec<T::AccountId> = Vec::new();609        <ApprovedList<T>>::insert(collection_id, item_id, itm);610611        Ok(())612    }613614    fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {615        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());616        if list_exists {617            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());618            let item_contains = list.contains(&item_index.clone());619620            if !item_contains {621                list.push(item_index.clone());622            }623624            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);625        } else {626            let mut itm = Vec::new();627            itm.push(item_index.clone());628            <AddressTokens<T>>::insert(collection_id, owner, itm);629        }630631        Ok(())632    }633634    fn remove_token_index(635        collection_id: u64,636        item_index: u64,637        owner: T::AccountId,638    ) -> DispatchResult {639        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());640        if list_exists {641            let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());642            let item_contains = list.contains(&item_index.clone());643644            if item_contains {645                list.retain(|&item| item != item_index);646                <AddressTokens<T>>::insert(collection_id, owner, list);647            }648        }649650        Ok(())651    }652653    fn move_token_index(654        collection_id: u64,655        item_index: u64,656        old_owner: T::AccountId,657        new_owner: T::AccountId,658    ) -> DispatchResult {659        Self::remove_token_index(collection_id, item_index, old_owner)?;660        Self::add_token_index(collection_id, item_index, new_owner)?;661662        Ok(())663    }664}665666667////////////////////////////////////////////////////////////////////////////////////////////////////668// Economic models669670/// Fee multiplier.671pub type Multiplier = FixedU128;672673type BalanceOf<T> =674	<<T as transaction_payment::Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;675type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<676	<T as system::Trait>::AccountId,>>::NegativeImbalance;677678679680/// Require the transactor pay for themselves and maybe include a tip to gain additional priority681/// in the queue.682#[derive(Encode, Decode, Clone, Eq, PartialEq)]683pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(#[codec(compact)] BalanceOf<T>);684685impl<T:Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug for ChargeTransactionPayment<T> {686	#[cfg(feature = "std")]687	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {688		write!(f, "ChargeTransactionPayment<{:?}>", self.0)689	}690	#[cfg(not(feature = "std"))]691	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {692		Ok(())693	}694}695696impl<T:Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T> where697	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,698	BalanceOf<T>: Send + Sync + FixedPointOperand,699{700	/// utility constructor. Used only in client/factory code.701	pub fn from(fee: BalanceOf<T>) -> Self {702		Self(fee)703	}704705    pub fn traditional_fee(706        len: usize,707        info: &DispatchInfoOf<T::Call>,708        tip: BalanceOf<T>,709    ) -> BalanceOf<T> where710        T::Call: Dispatchable<Info=DispatchInfo>,711    {712        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)713    }714715	fn withdraw_fee(716		&self,717        who: &T::AccountId,718        call: &T::Call,719		info: &DispatchInfoOf<T::Call>,720		len: usize,721	) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {722        let tip = self.0;723724        // Set fee based on call type. Creating collection costs 1 Unique.725        // All other transactions have traditional fees so far726        let fee = match call.is_sub_type() {727            Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),728            _ => Self::traditional_fee(len, info, tip)729730            // Flat fee model, use only for testing purposes731            // _ => <BalanceOf<T>>::from(100)732        };733734        // Determine who is paying transaction fee based on ecnomic model735        // Parse call to extract collection ID and access collection sponsor736        let sponsor: T::AccountId = match call.is_sub_type() {737            Some(Call::create_item(collection_id, _properties, _owner)) => {738                <Collection<T>>::get(collection_id).sponsor739            },740            Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {741                <Collection<T>>::get(collection_id).sponsor742            },743744            _ => T::AccountId::default()745        };746747        let mut who_pays_fee: T::AccountId = sponsor.clone();748        if sponsor == T::AccountId::default() {749            who_pays_fee = who.clone();750        }751752		// Only mess with balances if fee is not zero.753		if fee.is_zero() {754			return Ok((fee, None));755		}756757		match <T as transaction_payment::Trait>::Currency::withdraw(758			&who_pays_fee,759			fee,760			if tip.is_zero() {761				WithdrawReason::TransactionPayment.into()762			} else {763				WithdrawReason::TransactionPayment | WithdrawReason::Tip764			},765			ExistenceRequirement::KeepAlive,766		) {767			Ok(imbalance) => Ok((fee, Some(imbalance))),768			Err(_) => Err(InvalidTransaction::Payment.into()),769		}770	}771}772773impl<T:Trait + transaction_payment::Trait + Send + Sync> SignedExtension for ChargeTransactionPayment<T> where774    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,775    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Module<T>, T>,776{777	const IDENTIFIER: &'static str = "ChargeTransactionPayment";778	type AccountId = T::AccountId;779	type Call = T::Call;780	type AdditionalSigned = ();781	type Pre = (BalanceOf<T>, Self::AccountId, Option<NegativeImbalanceOf<T>>, BalanceOf<T>);782	fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> { Ok(()) }783784	fn validate(785		&self,786		who: &Self::AccountId,787		call: &Self::Call,788		info: &DispatchInfoOf<Self::Call>,789		len: usize,790	) -> TransactionValidity {791		let (fee, _) = self.withdraw_fee(who, call, info, len)?;792793		let mut r = ValidTransaction::default();794		// NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which795		// will be a bit more than setting the priority to tip. For now, this is enough.796		r.priority = fee.saturated_into::<TransactionPriority>();797		Ok(r)798	}799800	fn pre_dispatch(801		self,802		who: &Self::AccountId,803		call: &Self::Call,804		info: &DispatchInfoOf<Self::Call>,805		len: usize806	) -> Result<Self::Pre, TransactionValidityError> {807		let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;808		Ok((self.0, who.clone(), imbalance, fee))809	}810811	fn post_dispatch(812		pre: Self::Pre,813		info: &DispatchInfoOf<Self::Call>,814		post_info: &PostDispatchInfoOf<Self::Call>,815		len: usize,816		_result: &DispatchResult,817	) -> Result<(), TransactionValidityError> {818		let (tip, who, imbalance, fee) = pre;819		if let Some(payed) = imbalance {820			let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(821				len as u32,822				info,823				post_info,824				tip,825			);826			let refund = fee.saturating_sub(actual_fee);827			let actual_payment = match <T as transaction_payment::Trait>::Currency::deposit_into_existing(&who, refund) {828				Ok(refund_imbalance) => {829					// The refund cannot be larger than the up front payed max weight.830					// `PostDispatchInfo::calc_unspent` guards against such a case.831					match payed.offset(refund_imbalance) {832						Ok(actual_payment) => actual_payment,833						Err(_) => return Err(InvalidTransaction::Payment.into()),834					}835				}836				// We do not recreate the account using the refund. The up front payment837				// is gone in that case.838				Err(_) => payed,839			};840			let imbalances = actual_payment.split(tip);841			<T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(Some(imbalances.0).into_iter()842				.chain(Some(imbalances.1)));843		}844		Ok(())845	}846}