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

difftreelog

source

pallets/nft/src/lib.rs30.0 KiBsourcehistory
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}