git.delta.rocks / unique-network / refs/commits / 84a79a5d2b60

difftreelog

source

pallets/contract-helpers/src/lib.rs7.2 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 pallet_contracts::chain_extension::UncheckedFrom;11	use sp_runtime::{12		traits::{DispatchInfoOf, Hash, PostDispatchInfoOf, SignedExtension},13		transaction_validity,14	};15	use sp_std::vec::Vec;16	use up_sponsorship::SponsorshipHandler;1718	#[pallet::error]19	pub enum Error<T> {20		/// Should be contract owner21		NoPermission,22	}2324	#[pallet::config]25	pub trait Config: frame_system::Config + pallet_contracts::Config {}2627	#[pallet::pallet]28	#[pallet::generate_store(pub(super) trait Store)]29	pub struct Pallet<T>(_);3031	#[pallet::storage]32	pub(super) type Owner<T: Config> = StorageMap<33		Hasher = Twox128,34		Key = T::AccountId,35		Value = T::AccountId,36		QueryKind = ValueQuery,37	>;3839	#[pallet::storage]40	pub(super) type AllowlistEnabled<T: Config> =41		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;4243	#[pallet::storage]44	pub(super) type Allowlist<T: Config> = StorageDoubleMap<45		Hasher1 = Twox128,46		Key1 = T::AccountId,47		Hasher2 = Twox64Concat,48		Key2 = T::AccountId,49		Value = bool,50		QueryKind = ValueQuery,51	>;5253	#[pallet::storage]54	pub(super) type SelfSponsoring<T: Config> =55		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;5657	#[pallet::storage]58	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<59		Hasher = Twox128,60		Key = T::AccountId,61		Value = T::BlockNumber,62		QueryKind = ValueQuery,63	>;6465	#[pallet::storage]66	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<67		Hasher1 = Twox128,68		Key1 = T::AccountId,69		Hasher2 = Twox128,70		Key2 = T::AccountId,71		Value = T::BlockNumber,72		QueryKind = ValueQuery,73	>;7475	#[pallet::call]76	impl<T: Config> Pallet<T> {77		#[pallet::weight(0)]78		pub fn toggle_sponsoring(79			origin: OriginFor<T>,80			contract: T::AccountId,81			sponsoring: bool,82		) -> DispatchResult {83			let sender = ensure_signed(origin)?;84			ensure!(85				<Owner<T>>::get(&contract) == sender,86				<Error<T>>::NoPermission87			);8889			if sponsoring {90				<SelfSponsoring<T>>::insert(contract, true);91			} else {92				<SelfSponsoring<T>>::remove(contract);93			}94			Ok(())95		}9697		#[pallet::weight(0)]98		pub fn toggle_allowlist(99			origin: OriginFor<T>,100			contract: T::AccountId,101			enabled: bool,102		) -> DispatchResult {103			let sender = ensure_signed(origin)?;104			ensure!(105				<Owner<T>>::get(&contract) == sender,106				<Error<T>>::NoPermission107			);108109			if enabled {110				<AllowlistEnabled<T>>::insert(contract, true);111			} else {112				<AllowlistEnabled<T>>::remove(contract);113			}114			Ok(())115		}116117		#[pallet::weight(0)]118		pub fn toggle_allowed(119			origin: OriginFor<T>,120			contract: T::AccountId,121			user: T::AccountId,122			allowed: bool,123		) -> DispatchResult {124			let sender = ensure_signed(origin)?;125			ensure!(126				<Owner<T>>::get(&contract) == sender,127				<Error<T>>::NoPermission128			);129130			if allowed {131				<Allowlist<T>>::insert(contract, user, true);132			} else {133				<Allowlist<T>>::remove(contract, user);134			}135			Ok(())136		}137138		#[pallet::weight(0)]139		pub fn set_sponsoring_rate_limit(140			origin: OriginFor<T>,141			contract: T::AccountId,142			rate_limit: T::BlockNumber,143		) -> DispatchResult {144			let sender = ensure_signed(origin)?;145			ensure!(146				<Owner<T>>::get(&contract) == sender,147				<Error<T>>::NoPermission148			);149150			<SponsoringRateLimit<T>>::insert(contract, rate_limit);151			Ok(())152		}153	}154155	#[derive(Encode, Decode, Clone, PartialEq, Eq)]156	pub struct ContractHelpersExtension<T>(PhantomData<T>);157	impl<T> core::fmt::Debug for ContractHelpersExtension<T> {158		fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {159			fmt.debug_struct("ContractHelpersExtension").finish()160		}161	}162163	type CodeHash<T> = <T as frame_system::Config>::Hash;164	impl<T> SignedExtension for ContractHelpersExtension<T>165	where166		T: Config + Send + Sync,167		T::Call: sp_runtime::traits::Dispatchable,168		T::Call: IsSubType<pallet_contracts::Call<T>>,169		T::AccountId: UncheckedFrom<T::Hash>,170		T::AccountId: AsRef<[u8]>,171	{172		const IDENTIFIER: &'static str = "ContractHelpers";173		type AccountId = T::AccountId;174		type Call = T::Call;175		type AdditionalSigned = ();176		type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;177178		fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {179			Ok(())180		}181182		fn validate(183			&self,184			who: &T::AccountId,185			call: &Self::Call,186			_info: &DispatchInfoOf<Self::Call>,187			_len: usize,188		) -> transaction_validity::TransactionValidity {189			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =190				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)191			{192				let called_contract: T::AccountId =193					T::Lookup::lookup((*dest).clone()).unwrap_or_default();194				if <AllowlistEnabled<T>>::get(&called_contract)195					&& !<Allowlist<T>>::get(&called_contract, who)196					&& &<Owner<T>>::get(&called_contract) != who197				{198					return Err(transaction_validity::InvalidTransaction::Call.into());199				}200			}201			Ok(transaction_validity::ValidTransaction::default())202		}203204		fn pre_dispatch(205			self,206			who: &Self::AccountId,207			call: &Self::Call,208			_info: &DispatchInfoOf<Self::Call>,209			_len: usize,210		) -> Result<Self::Pre, TransactionValidityError> {211			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {212				Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {213					Ok(Some((who.clone(), *code_hash, salt.clone())))214				}215				Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {216					let code_hash = &T::Hashing::hash(code);217					Ok(Some((who.clone(), *code_hash, salt.clone())))218				}219				_ => Ok(None),220			}221		}222223		fn post_dispatch(224			pre: Self::Pre,225			_info: &DispatchInfoOf<Self::Call>,226			_post_info: &PostDispatchInfoOf<Self::Call>,227			_len: usize,228			_result: &DispatchResult,229		) -> Result<(), TransactionValidityError> {230			if let Some((who, code_hash, salt)) = pre {231				let new_contract_address =232					<pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);233				<Owner<T>>::insert(&new_contract_address, &who);234			}235236			Ok(())237		}238	}239240	pub struct ContractSponsorshipHandler<T>(PhantomData<T>);241	impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>242	where243		T: Config,244		C: IsSubType<pallet_contracts::Call<T>>,245		T::AccountId: UncheckedFrom<T::Hash>,246		T::AccountId: AsRef<[u8]>,247	{248		fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {249			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =250				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)251			{252				let called_contract: T::AccountId =253					T::Lookup::lookup((*dest).clone()).unwrap_or_default();254				if <SelfSponsoring<T>>::get(&called_contract) {255					let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);256					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;257					let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);258					let limit_time = last_tx_block + rate_limit;259260					if block_number >= limit_time {261						SponsorBasket::<T>::insert(&called_contract, who, block_number);262						return Some(called_contract);263					}264				}265			}266			None267		}268	}269}