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

difftreelog

source

pallets/nft-transaction-payment/src/lib.rs16.1 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![cfg_attr(not(feature = "std"), no_std)]78#[cfg(feature = "std")]9pub use std::*;1011#[cfg(feature = "std")]12pub use serde::*;1314#[cfg(feature = "runtime-benchmarks")]15mod benchmarking;1617#[cfg(test)]18mod tests;1920use frame_support::{21	decl_error, decl_module, decl_storage,22	traits::{23		IsSubType, 24	},25	weights::{26		DispatchInfo27	}28};29use sp_runtime::traits::StaticLookup;30use sp_runtime::{31	traits::{ 32		Hash, Dispatchable,33	},34};35use pallet_contracts::chain_extension::UncheckedFrom;36use sp_std::prelude::*;37use nft_data_structs::{38	CreateItemData,39	CollectionId, CollectionMode, TokenId40};4142type CodeHash<T> = <T as frame_system::Config>::Hash;4344	pub trait Config: frame_system::Config + pallet_contracts::Config + pallet_transaction_payment::Config + pallet_nft::Config {45	}4647	// Error for non-fungible-token module.48	49	decl_error! {50		/// Error for non-fungible-token module.51		pub enum Error for Module<T: Config> {52		/// No available class ID53		NoAvailableClassId,54		/// No available token ID55		NoAvailableTokenId,56		/// Token(ClassId, TokenId) not found57		TokenNotFound,58		/// Class not found59		CollectionNotFound,60		/// The operator is not the owner of the token and has no permission61		NoPermission,62		/// Arithmetic calculation overflow63		NumOverflow,64		/// Can not destroy class65		/// Total issuance is not 066		CannotDestroyClass,67	}68}6970	decl_storage! {71		trait Store for Module<T: Config> as NftTransactionPayment{7273	}74}7576decl_module! {7778	pub struct Module<T: Config> for enum Call 79    where 80		origin: T::Origin,81    {82	}83}8485impl<T: Config> Module<T> 86{8788	pub fn withdraw_type(89		who: &T::AccountId,90		call: &T::Call91	) -> Option<T::AccountId> where 92		T::Call: Dispatchable<Info=DispatchInfo>,93		T::Call: IsSubType<pallet_nft::Call<T>>, 94		T::Call: IsSubType<pallet_contracts::Call<T>>,95		T::AccountId: AsRef<[u8]>,96		T::AccountId: UncheckedFrom<T::Hash>97		{9899        let mut sponsor: Option<T::AccountId> = match IsSubType::<pallet_nft::Call<T>>::is_sub_type(call)  {100            Some(pallet_nft::Call::create_item(collection_id, _owner, _properties)) => {101102                Self::withdraw_create_item(who, collection_id, &_properties)103            },104            Some(pallet_nft::Call::transfer(_new_owner, collection_id, item_id, _value)) => {105106                Self::withdraw_transfer(who, collection_id, item_id)107            },108            Some(pallet_nft::Call::set_variable_meta_data(collection_id, item_id, data)) => {109110                Self::withdraw_set_variable_meta_data(who, collection_id, item_id, &data)111			},112			_ => None,113        };114115        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {116            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {117118                Self::withdraw_contract_call(who, dest)119            },120            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {121122                Self::withdraw_contract_instantiate(&who, code_hash, salt)123            },124            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {125126                Self::withdraw_contract_instantiate(&who, &T::Hashing::hash(&_code), _salt)127			},128			_ => None,129        });130131		sponsor132	}133134135136	pub fn withdraw_create_item(137		who: &T::AccountId,138		collection_id: &CollectionId,139		_properties: &CreateItemData,140	) -> Option<T::AccountId> {141	142		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;143144		// sponsor timeout145		let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;146147		let limit = collection.limits.sponsor_transfer_timeout;148		if pallet_nft::CreateItemBasket::<T>::contains_key((collection_id, &who)) {149			let last_tx_block = pallet_nft::CreateItemBasket::<T>::get((collection_id, &who));150			let limit_time = last_tx_block + limit.into();151			if block_number <= limit_time {152				return None;153			}154		}155		pallet_nft::CreateItemBasket::<T>::insert((collection_id, who.clone()), block_number);156157		// check free create limit158		if collection.limits.sponsored_data_size >= (_properties.len() as u32) {159			collection.sponsorship.sponsor()160				.cloned()161		} else {162			None163		}164	}165166	pub fn withdraw_transfer(167		who: &T::AccountId,168		collection_id: &CollectionId,169		item_id: &TokenId,170	) -> Option<T::AccountId> {171172		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;173		let limits = pallet_nft::ChainLimit::get();174175		let mut sponsor_transfer = false;176		if collection.sponsorship.confirmed() {177178			let collection_limits = collection.limits.clone();179			let collection_mode = collection.mode.clone();180181			// sponsor timeout182			let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;183			sponsor_transfer = match collection_mode {184				CollectionMode::NFT => {185186					// get correct limit187					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {188						collection_limits.sponsor_transfer_timeout189					} else {190						limits.nft_sponsor_transfer_timeout191					};192193					let mut sponsored = true;194					if pallet_nft::NftTransferBasket::<T>::contains_key(collection_id, item_id) {195						let last_tx_block = pallet_nft::NftTransferBasket::<T>::get(collection_id, item_id);196						let limit_time = last_tx_block + limit.into();197						if block_number <= limit_time {198							sponsored = false;199						}200					}201					if sponsored {202						pallet_nft::NftTransferBasket::<T>::insert(collection_id, item_id, block_number);203					}204205					sponsored206				}207				CollectionMode::Fungible(_) => {208209					// get correct limit210					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {211						collection_limits.sponsor_transfer_timeout212					} else {213						limits.fungible_sponsor_transfer_timeout214					};215216					let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;217					let mut sponsored = true;218					if pallet_nft::FungibleTransferBasket::<T>::contains_key(collection_id, who) {219						let last_tx_block = pallet_nft::FungibleTransferBasket::<T>::get(collection_id, who);220						let limit_time = last_tx_block + limit.into();221						if block_number <= limit_time {222							sponsored = false;223						}224					}225					if sponsored {226						pallet_nft::FungibleTransferBasket::<T>::insert(collection_id, who, block_number);227					}228229					sponsored230				}231				CollectionMode::ReFungible => {232233					// get correct limit234					let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {235						collection_limits.sponsor_transfer_timeout236					} else {237						limits.refungible_sponsor_transfer_timeout238					};239240					let mut sponsored = true;241					if pallet_nft::ReFungibleTransferBasket::<T>::contains_key(collection_id, item_id) {242						let last_tx_block = pallet_nft::ReFungibleTransferBasket::<T>::get(collection_id, item_id);243						let limit_time = last_tx_block + limit.into();244						if block_number <= limit_time {245							sponsored = false;246						}247					}248					if sponsored {249						pallet_nft::ReFungibleTransferBasket::<T>::insert(collection_id, item_id, block_number);250					}251252					sponsored253				}254				_ => {255					false256				},257			};258		}259260		if !sponsor_transfer {261			None262		} else {263			collection.sponsorship.sponsor()264				.cloned()265		}266	}267	268	pub fn withdraw_set_variable_meta_data(269		who: &T::AccountId,270		collection_id: &CollectionId,271		item_id: &TokenId,272		data: &Vec<u8>,273	) -> Option<T::AccountId> {274275		let mut sponsor_metadata_changes = false;276277		let collection = pallet_nft::CollectionById::<T>::get(collection_id)?;278279		if280			collection.sponsorship.confirmed() &&281			// Can't sponsor fungible collection, this tx will be rejected282			// as invalid283			!matches!(collection.mode, CollectionMode::Fungible(_)) &&284			data.len() <= collection.limits.sponsored_data_size as usize285		{286			if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {287				let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;288289				if pallet_nft::VariableMetaDataBasket::<T>::get(collection_id, item_id)290					.map(|last_block| block_number - last_block > rate_limit)291					.unwrap_or(true) 292				{293					sponsor_metadata_changes = true;294					pallet_nft::VariableMetaDataBasket::<T>::insert(collection_id, item_id, block_number);295				}296			}297		}298299		if !sponsor_metadata_changes {300			None301		} else {302			collection.sponsorship.sponsor().cloned()303		}304305	}306307	pub fn withdraw_contract_call(308		who: &T::AccountId,309		dest: &<T::Lookup as StaticLookup>::Source310	) -> Option<T::AccountId> {311312		let called_contract: T::AccountId = T::Lookup::lookup((dest).clone()).unwrap_or(T::AccountId::default());313314		let owned_contract = pallet_nft::ContractOwner::<T>::get(called_contract.clone()).as_ref() == Some(who);315		let white_list_enabled = pallet_nft::ContractWhiteListEnabled::<T>::contains_key(called_contract.clone());316		  317		// ???318		if !owned_contract && white_list_enabled {319		 	if !pallet_nft::ContractWhiteList::<T>::contains_key(called_contract.clone(), who) {320				return Some(who.clone())321		 		// return Err(InvalidTransaction::Call.into());322		 	}323		}324325		let mut sponsor_transfer = false;326		if pallet_nft::ContractSponsoringRateLimit::<T>::contains_key(called_contract.clone()) {327			let last_tx_block = pallet_nft::ContractSponsorBasket::<T>::get((&called_contract, &who));328			let block_number = <frame_system::Module<T>>::block_number() as T::BlockNumber;329			let rate_limit = pallet_nft::ContractSponsoringRateLimit::<T>::get(&called_contract);330			let limit_time = last_tx_block + rate_limit;331332			if block_number >= limit_time {333				pallet_nft::ContractSponsorBasket::<T>::insert((called_contract.clone(), who.clone()), block_number);334				sponsor_transfer = true;335			}336		} else {337			sponsor_transfer = false;338		}339	   340		if sponsor_transfer {341			if pallet_nft::ContractSelfSponsoring::<T>::contains_key(called_contract.clone()) {342				if pallet_nft::ContractSelfSponsoring::<T>::get(called_contract.clone()) {343					return Some(called_contract);344				}345			}346		}347348		None349	}350351	pub fn withdraw_contract_instantiate(352		who: &T::AccountId,353		code_hash: &CodeHash<T>,354		salt: &[u8],355	) -> Option<T::AccountId> where356	T::AccountId: AsRef<[u8]>,357	T::AccountId: UncheckedFrom<T::Hash>358	{359360		let new_contract_address = <pallet_contracts::Module<T>>::contract_address(361			&who,362			code_hash,363			salt,364		);365		pallet_nft::ContractOwner::<T>::insert(new_contract_address.clone(), who.clone());366367		None368	}369}370371// type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;372373// /// Require the transactor pay for themselves and maybe include a tip to gain additional priority374// /// in the queue.375// #[derive(Encode, Decode, Clone, Eq, PartialEq)]376// pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);377378// impl<T: Config + Send + Sync> sp_std::fmt::Debug 379//     for ChargeTransactionPayment<T>380// {381// 	#[cfg(feature = "std")]382// 	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {383// 		write!(f, "ChargeTransactionPayment<{:?}>", self.0)384// 	}385// 	#[cfg(not(feature = "std"))]386// 	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {387// 		Ok(())388// 	}389// }390391// impl<T: Config> ChargeTransactionPayment<T>392// where393// 	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,394//     BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,395//     T::AccountId: AsRef<[u8]>,396//     T::AccountId: UncheckedFrom<T::Hash>,397// {398//     fn traditional_fee(399//         len: usize,400//         info: &DispatchInfoOf<T::Call>,401//         tip: BalanceOf<T>,402//     ) -> BalanceOf<T>403//     where404//         T::Call: Dispatchable<Info = DispatchInfo>,405//     {406//         <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)407//     }408409// 	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {410//         let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);411//         let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);412//         let len_saturation = max_block_length as u64 / (len as u64).max(1);413//         let coefficient: BalanceOf<T> = weight_saturation414//             .min(len_saturation)415//             .saturated_into::<BalanceOf<T>>();416//         final_fee417//             .saturating_mul(coefficient)418//             .saturated_into::<TransactionPriority>()419//     }420421//     fn withdraw_fee(422//         &self,423//         who: &T::AccountId,424//         call: &T::Call,425//         info: &DispatchInfoOf<T::Call>,426//         len: usize,427// 	) -> Result<428// 		(429// 			BalanceOf<T>,430// 			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,431// 		),432// 		TransactionValidityError,433// 	> {434//         let tip = self.0;435436//         let fee = Self::traditional_fee(len, info, tip);437438//         // Only mess with balances if fee is not zero.439//         if fee.is_zero() {440//             return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)441// 			.map(|i| (fee, i));442//         }443444//         // Determine who is paying transaction fee based on ecnomic model445// 		// Parse call to extract collection ID and access collection sponsor446		447// 		let sponsor = pallet_nft_transaction_payment::<T>::withdraw_type(who, call);448// 		//let sponsor = Self::Module::<T>::withdraw_type(who, call);;449// 		// <pallet_nft_transaction_payment::Module<T>>::withdraw_type(who, call);450451//         let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());452453// 		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)454// 			.map(|i| (fee, i))455//     }456// }457458459// impl<T: Config + Send + Sync> SignedExtension460//     for ChargeTransactionPayment<T>461// where462//     BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,463// 	T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<pallet_nft::Call<T>> + IsSubType<pallet_contracts::Call<T>>,464//     T::AccountId: AsRef<[u8]>,465//     T::AccountId: UncheckedFrom<T::Hash>,466// {467//     const IDENTIFIER: &'static str = "ChargeTransactionPayment";468//     type AccountId = T::AccountId;469//     type Call = T::Call;470//     type AdditionalSigned = ();471//     type Pre = (472//         // tip473//         BalanceOf<T>,474//         // who pays fee475//         Self::AccountId,476// 		// imbalance resulting from withdrawing the fee477// 		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,478//     );479//     fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {480//         Ok(())481//     }482483//     fn validate(484//         &self,485//         who: &Self::AccountId,486//         call: &Self::Call,487//         info: &DispatchInfoOf<Self::Call>,488//         len: usize,489//     ) -> TransactionValidity {490// 		let (fee, _) = self.withdraw_fee(who, call, info, len)?;491// 		Ok(ValidTransaction {492// 			priority: Self::get_priority(len, info, fee),493// 			..Default::default()494// 		})495//     }496497//     fn pre_dispatch(498//         self,499//         who: &Self::AccountId,500//         call: &Self::Call,501//         info: &DispatchInfoOf<Self::Call>,502//         len: usize,503//     ) -> Result<Self::Pre, TransactionValidityError> {504//         let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;505//         Ok((self.0, who.clone(), imbalance))506//     }507508//     fn post_dispatch(509//         pre: Self::Pre,510//         info: &DispatchInfoOf<Self::Call>,511//         post_info: &PostDispatchInfoOf<Self::Call>,512//         len: usize,513//         _result: &DispatchResult,514//     ) -> Result<(), TransactionValidityError> {515// 		let (tip, who, imbalance) = pre;516// 		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(517// 			len as u32,518// 			info,519// 			post_info,520// 			tip,521// 		);522// 		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;523// 		Ok(())524//     }525// }