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

difftreelog

source

pallets/contract-helpers/src/lib.rs7.8 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, scale_info::TypeInfo)]169	pub struct ContractHelpersExtension<T: scale_info::TypeInfo>(PhantomData<T>);170	impl<T: scale_info::TypeInfo> 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: scale_info::TypeInfo> 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 {203				dest,204				value: _value,205				gas_limit: _gas_limit,206				data: _data,207			}) = IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)208			{209				let called_contract: T::AccountId =210					T::Lookup::lookup((*dest).clone()).unwrap_or_default();211				if !<Pallet<T>>::allowed(called_contract, who.clone(), true) {212					return Err(transaction_validity::InvalidTransaction::Call.into());213				}214			}215			Ok(transaction_validity::ValidTransaction::default())216		}217218		fn pre_dispatch(219			self,220			who: &Self::AccountId,221			call: &Self::Call,222			_info: &DispatchInfoOf<Self::Call>,223			_len: usize,224		) -> Result<Self::Pre, TransactionValidityError> {225			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {226				Some(pallet_contracts::Call::instantiate {227					code_hash, salt, ..228				}) => Ok(Some((who.clone(), *code_hash, salt.clone()))),229				Some(pallet_contracts::Call::instantiate_with_code { code, salt, .. }) => {230					let code_hash = &T::Hashing::hash(code);231					Ok(Some((who.clone(), *code_hash, salt.clone())))232				}233				_ => Ok(None),234			}235		}236237		fn post_dispatch(238			pre: Self::Pre,239			_info: &DispatchInfoOf<Self::Call>,240			_post_info: &PostDispatchInfoOf<Self::Call>,241			_len: usize,242			_result: &DispatchResult,243		) -> Result<(), TransactionValidityError> {244			if let Some((who, code_hash, salt)) = pre {245				let new_contract_address =246					<pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);247				<Owner<T>>::insert(&new_contract_address, &who);248			}249250			Ok(())251		}252	}253254	pub struct ContractSponsorshipHandler<T>(PhantomData<T>);255	impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>256	where257		T: Config,258		C: IsSubType<pallet_contracts::Call<T>>,259		T::AccountId: UncheckedFrom<T::Hash>,260		T::AccountId: AsRef<[u8]>,261	{262		fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {263			if let Some(pallet_contracts::Call::call { dest, .. }) =264				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)265			{266				let called_contract: T::AccountId =267					T::Lookup::lookup((*dest).clone()).unwrap_or_default();268				if <SelfSponsoring<T>>::get(&called_contract)269					&& <Pallet<T>>::allowed(called_contract.clone(), who.clone(), false)270				{271					let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);272					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;273					let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);274					let limit_time = last_tx_block + rate_limit;275276					if block_number >= limit_time {277						SponsorBasket::<T>::insert(&called_contract, who, block_number);278						return Some(called_contract);279					}280				}281			}282			None283		}284	}285}