git.delta.rocks / unique-network / refs/commits / 50b15345cfd5

difftreelog

source

pallets/contract-helpers/src/lib.rs7.7 KiBsourcehistory
1#![cfg_attr(not(feature = "std"), no_std)]23pub use pallet::*;45#[frame_support::pallet]6pub mod pallet {7	use frame_support::sp_runtime::traits::StaticLookup;8	use frame_support::{pallet_prelude::*, traits::IsSubType};9	use frame_system::pallet_prelude::*;10	use frame_system::Config as SysConfig;11	use pallet_contracts::chain_extension::UncheckedFrom;12	use sp_runtime::{13		traits::{DispatchInfoOf, Hash, PostDispatchInfoOf, SignedExtension},14		transaction_validity,15	};16	use sp_std::vec::Vec;17	use up_sponsorship::SponsorshipHandler;1819	#[pallet::error]20	pub enum Error<T> {21		/// Should be contract owner22		NoPermission,23	}2425	#[pallet::config]26	pub trait Config: frame_system::Config + pallet_contracts::Config {27		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;28	}2930	#[pallet::pallet]31	#[pallet::generate_store(pub(super) trait Store)]32	pub struct Pallet<T>(_);3334	#[pallet::storage]35	pub(super) type Owner<T: Config> = StorageMap<36		Hasher = Twox128,37		Key = T::AccountId,38		Value = T::AccountId,39		QueryKind = ValueQuery,40	>;4142	#[pallet::storage]43	pub(super) type AllowlistEnabled<T: Config> =44		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;4546	#[pallet::storage]47	pub(super) type Allowlist<T: Config> = StorageDoubleMap<48		Hasher1 = Twox128,49		Key1 = T::AccountId,50		Hasher2 = Twox64Concat,51		Key2 = T::AccountId,52		Value = bool,53		QueryKind = ValueQuery,54	>;5556	#[pallet::storage]57	pub(super) type SelfSponsoring<T: Config> =58		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;5960	#[pallet::storage]61	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<62		Hasher = Twox128,63		Key = T::AccountId,64		Value = T::BlockNumber,65		QueryKind = ValueQuery,66		OnEmpty = T::DefaultSponsoringRateLimit,67	>;6869	#[pallet::storage]70	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<71		Hasher1 = Twox128,72		Key1 = T::AccountId,73		Hasher2 = Twox128,74		Key2 = T::AccountId,75		Value = T::BlockNumber,76		QueryKind = ValueQuery,77	>;7879	impl<T: Config> Pallet<T> {80		pub fn allowed(contract: T::AccountId, user: T::AccountId, default: bool) -> bool {81			if !<AllowlistEnabled<T>>::get(&contract) {82				return default;83			}84			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(contract) == user85		}86	}8788	#[pallet::call]89	impl<T: Config> Pallet<T> {90		#[pallet::weight(0)]91		pub fn toggle_sponsoring(92			origin: OriginFor<T>,93			contract: T::AccountId,94			sponsoring: bool,95		) -> DispatchResult {96			let sender = ensure_signed(origin)?;97			ensure!(98				<Owner<T>>::get(&contract) == sender,99				<Error<T>>::NoPermission100			);101102			if sponsoring {103				<SelfSponsoring<T>>::insert(contract, true);104			} else {105				<SelfSponsoring<T>>::remove(contract);106			}107			Ok(())108		}109110		#[pallet::weight(0)]111		pub fn toggle_allowlist(112			origin: OriginFor<T>,113			contract: T::AccountId,114			enabled: bool,115		) -> DispatchResult {116			let sender = ensure_signed(origin)?;117			ensure!(118				<Owner<T>>::get(&contract) == sender,119				<Error<T>>::NoPermission120			);121122			if enabled {123				<AllowlistEnabled<T>>::insert(contract, true);124			} else {125				<AllowlistEnabled<T>>::remove(contract);126			}127			Ok(())128		}129130		#[pallet::weight(0)]131		pub fn toggle_allowed(132			origin: OriginFor<T>,133			contract: T::AccountId,134			user: T::AccountId,135			allowed: bool,136		) -> DispatchResult {137			let sender = ensure_signed(origin)?;138			ensure!(139				<Owner<T>>::get(&contract) == sender,140				<Error<T>>::NoPermission141			);142143			if allowed {144				<Allowlist<T>>::insert(contract, user, true);145			} else {146				<Allowlist<T>>::remove(contract, user);147			}148			Ok(())149		}150151		#[pallet::weight(0)]152		pub fn set_sponsoring_rate_limit(153			origin: OriginFor<T>,154			contract: T::AccountId,155			rate_limit: T::BlockNumber,156		) -> DispatchResult {157			let sender = ensure_signed(origin)?;158			ensure!(159				<Owner<T>>::get(&contract) == sender,160				<Error<T>>::NoPermission161			);162163			<SponsoringRateLimit<T>>::insert(contract, rate_limit);164			Ok(())165		}166	}167168	#[derive(Encode, Decode, Clone, PartialEq, Eq)]169	pub struct ContractHelpersExtension<T>(PhantomData<T>);170	impl<T> core::fmt::Debug for ContractHelpersExtension<T> {171		fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {172			fmt.debug_struct("ContractHelpersExtension").finish()173		}174	}175176	type CodeHash<T> = <T as frame_system::Config>::Hash;177	impl<T> SignedExtension for ContractHelpersExtension<T>178	where179		T: Config + Send + Sync,180		<T as SysConfig>::Call: sp_runtime::traits::Dispatchable,181		<T as SysConfig>::Call: IsSubType<pallet_contracts::Call<T>>,182		T::AccountId: UncheckedFrom<T::Hash>,183		T::AccountId: AsRef<[u8]>,184	{185		const IDENTIFIER: &'static str = "ContractHelpers";186		type AccountId = T::AccountId;187		type Call = <T as SysConfig>::Call;188		type AdditionalSigned = ();189		type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;190191		fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {192			Ok(())193		}194195		fn validate(196			&self,197			who: &T::AccountId,198			call: &Self::Call,199			_info: &DispatchInfoOf<Self::Call>,200			_len: usize,201		) -> transaction_validity::TransactionValidity {202			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =203				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)204			{205				let called_contract: T::AccountId =206					T::Lookup::lookup((*dest).clone()).unwrap_or_default();207				if !<Pallet<T>>::allowed(called_contract, who.clone(), true) {208					return Err(transaction_validity::InvalidTransaction::Call.into());209				}210			}211			Ok(transaction_validity::ValidTransaction::default())212		}213214		fn pre_dispatch(215			self,216			who: &Self::AccountId,217			call: &Self::Call,218			_info: &DispatchInfoOf<Self::Call>,219			_len: usize,220		) -> Result<Self::Pre, TransactionValidityError> {221			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {222				Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {223					Ok(Some((who.clone(), *code_hash, salt.clone())))224				}225				Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {226					let code_hash = &T::Hashing::hash(code);227					Ok(Some((who.clone(), *code_hash, salt.clone())))228				}229				_ => Ok(None),230			}231		}232233		fn post_dispatch(234			pre: Self::Pre,235			_info: &DispatchInfoOf<Self::Call>,236			_post_info: &PostDispatchInfoOf<Self::Call>,237			_len: usize,238			_result: &DispatchResult,239		) -> Result<(), TransactionValidityError> {240			if let Some((who, code_hash, salt)) = pre {241				let new_contract_address =242					<pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);243				<Owner<T>>::insert(&new_contract_address, &who);244			}245246			Ok(())247		}248	}249250	pub struct ContractSponsorshipHandler<T>(PhantomData<T>);251	impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>252	where253		T: Config,254		C: IsSubType<pallet_contracts::Call<T>>,255		T::AccountId: UncheckedFrom<T::Hash>,256		T::AccountId: AsRef<[u8]>,257	{258		fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {259			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =260				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)261			{262				let called_contract: T::AccountId =263					T::Lookup::lookup((*dest).clone()).unwrap_or_default();264				if <SelfSponsoring<T>>::get(&called_contract)265					&& <Pallet<T>>::allowed(called_contract.clone(), who.clone(), false)266				{267					let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);268					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;269					let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);270					let limit_time = last_tx_block + rate_limit;271272					if block_number >= limit_time {273						SponsorBasket::<T>::insert(&called_contract, who, block_number);274						return Some(called_contract);275					}276				}277			}278			None279		}280	}281}