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

difftreelog

source

pallets/contract-helpers/src/lib.rs7.6 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 {26		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;27	}2829	#[pallet::pallet]30	#[pallet::generate_store(pub(super) trait Store)]31	pub struct Pallet<T>(_);3233	#[pallet::storage]34	pub(super) type Owner<T: Config> = StorageMap<35		Hasher = Twox128,36		Key = T::AccountId,37		Value = T::AccountId,38		QueryKind = ValueQuery,39	>;4041	#[pallet::storage]42	pub(super) type AllowlistEnabled<T: Config> =43		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;4445	#[pallet::storage]46	pub(super) type Allowlist<T: Config> = StorageDoubleMap<47		Hasher1 = Twox128,48		Key1 = T::AccountId,49		Hasher2 = Twox64Concat,50		Key2 = T::AccountId,51		Value = bool,52		QueryKind = ValueQuery,53	>;5455	#[pallet::storage]56	pub(super) type SelfSponsoring<T: Config> =57		StorageMap<Hasher = Twox128, Key = T::AccountId, Value = bool, QueryKind = ValueQuery>;5859	#[pallet::storage]60	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<61		Hasher = Twox128,62		Key = T::AccountId,63		Value = T::BlockNumber,64		QueryKind = ValueQuery,65		OnEmpty = T::DefaultSponsoringRateLimit,66	>;6768	#[pallet::storage]69	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<70		Hasher1 = Twox128,71		Key1 = T::AccountId,72		Hasher2 = Twox128,73		Key2 = T::AccountId,74		Value = T::BlockNumber,75		QueryKind = ValueQuery,76	>;7778	impl<T: Config> Pallet<T> {79		pub fn allowed(contract: T::AccountId, user: T::AccountId, default: bool) -> bool {80			if !<AllowlistEnabled<T>>::get(&contract) {81				return default;82			}83			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(contract) == user84		}85	}8687	#[pallet::call]88	impl<T: Config> Pallet<T> {89		#[pallet::weight(0)]90		pub fn toggle_sponsoring(91			origin: OriginFor<T>,92			contract: T::AccountId,93			sponsoring: bool,94		) -> DispatchResult {95			let sender = ensure_signed(origin)?;96			ensure!(97				<Owner<T>>::get(&contract) == sender,98				<Error<T>>::NoPermission99			);100101			if sponsoring {102				<SelfSponsoring<T>>::insert(contract, true);103			} else {104				<SelfSponsoring<T>>::remove(contract);105			}106			Ok(())107		}108109		#[pallet::weight(0)]110		pub fn toggle_allowlist(111			origin: OriginFor<T>,112			contract: T::AccountId,113			enabled: bool,114		) -> DispatchResult {115			let sender = ensure_signed(origin)?;116			ensure!(117				<Owner<T>>::get(&contract) == sender,118				<Error<T>>::NoPermission119			);120121			if enabled {122				<AllowlistEnabled<T>>::insert(contract, true);123			} else {124				<AllowlistEnabled<T>>::remove(contract);125			}126			Ok(())127		}128129		#[pallet::weight(0)]130		pub fn toggle_allowed(131			origin: OriginFor<T>,132			contract: T::AccountId,133			user: T::AccountId,134			allowed: bool,135		) -> DispatchResult {136			let sender = ensure_signed(origin)?;137			ensure!(138				<Owner<T>>::get(&contract) == sender,139				<Error<T>>::NoPermission140			);141142			if allowed {143				<Allowlist<T>>::insert(contract, user, true);144			} else {145				<Allowlist<T>>::remove(contract, user);146			}147			Ok(())148		}149150		#[pallet::weight(0)]151		pub fn set_sponsoring_rate_limit(152			origin: OriginFor<T>,153			contract: T::AccountId,154			rate_limit: T::BlockNumber,155		) -> DispatchResult {156			let sender = ensure_signed(origin)?;157			ensure!(158				<Owner<T>>::get(&contract) == sender,159				<Error<T>>::NoPermission160			);161162			<SponsoringRateLimit<T>>::insert(contract, rate_limit);163			Ok(())164		}165	}166167	#[derive(Encode, Decode, Clone, PartialEq, Eq)]168	pub struct ContractHelpersExtension<T>(PhantomData<T>);169	impl<T> core::fmt::Debug for ContractHelpersExtension<T> {170		fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {171			fmt.debug_struct("ContractHelpersExtension").finish()172		}173	}174175	type CodeHash<T> = <T as frame_system::Config>::Hash;176	impl<T> SignedExtension for ContractHelpersExtension<T>177	where178		T: Config + Send + Sync,179		T::Call: sp_runtime::traits::Dispatchable,180		T::Call: IsSubType<pallet_contracts::Call<T>>,181		T::AccountId: UncheckedFrom<T::Hash>,182		T::AccountId: AsRef<[u8]>,183	{184		const IDENTIFIER: &'static str = "ContractHelpers";185		type AccountId = T::AccountId;186		type Call = T::Call;187		type AdditionalSigned = ();188		type Pre = Option<(Self::AccountId, CodeHash<T>, Vec<u8>)>;189190		fn additional_signed(&self) -> Result<(), transaction_validity::TransactionValidityError> {191			Ok(())192		}193194		fn validate(195			&self,196			who: &T::AccountId,197			call: &Self::Call,198			_info: &DispatchInfoOf<Self::Call>,199			_len: usize,200		) -> transaction_validity::TransactionValidity {201			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =202				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)203			{204				let called_contract: T::AccountId =205					T::Lookup::lookup((*dest).clone()).unwrap_or_default();206				if !<Pallet<T>>::allowed(called_contract, who.clone(), true) {207					return Err(transaction_validity::InvalidTransaction::Call.into());208				}209			}210			Ok(transaction_validity::ValidTransaction::default())211		}212213		fn pre_dispatch(214			self,215			who: &Self::AccountId,216			call: &Self::Call,217			_info: &DispatchInfoOf<Self::Call>,218			_len: usize,219		) -> Result<Self::Pre, TransactionValidityError> {220			match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {221				Some(pallet_contracts::Call::instantiate(_, _, code_hash, _, salt)) => {222					Ok(Some((who.clone(), *code_hash, salt.clone())))223				}224				Some(pallet_contracts::Call::instantiate_with_code(_, _, code, _, salt)) => {225					let code_hash = &T::Hashing::hash(code);226					Ok(Some((who.clone(), *code_hash, salt.clone())))227				}228				_ => Ok(None),229			}230		}231232		fn post_dispatch(233			pre: Self::Pre,234			_info: &DispatchInfoOf<Self::Call>,235			_post_info: &PostDispatchInfoOf<Self::Call>,236			_len: usize,237			_result: &DispatchResult,238		) -> Result<(), TransactionValidityError> {239			if let Some((who, code_hash, salt)) = pre {240				let new_contract_address =241					<pallet_contracts::Pallet<T>>::contract_address(&who, &code_hash, &salt);242				<Owner<T>>::insert(&new_contract_address, &who);243			}244245			Ok(())246		}247	}248249	pub struct ContractSponsorshipHandler<T>(PhantomData<T>);250	impl<T, C> SponsorshipHandler<T::AccountId, C> for ContractSponsorshipHandler<T>251	where252		T: Config,253		C: IsSubType<pallet_contracts::Call<T>>,254		T::AccountId: UncheckedFrom<T::Hash>,255		T::AccountId: AsRef<[u8]>,256	{257		fn get_sponsor(who: &T::AccountId, call: &C) -> Option<T::AccountId> {258			if let Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) =259				IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call)260			{261				let called_contract: T::AccountId =262					T::Lookup::lookup((*dest).clone()).unwrap_or_default();263				if <SelfSponsoring<T>>::get(&called_contract)264					&& <Pallet<T>>::allowed(called_contract.clone(), who.clone(), false)265				{266					let last_tx_block = SponsorBasket::<T>::get(&called_contract, &who);267					let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;268					let rate_limit = SponsoringRateLimit::<T>::get(&called_contract);269					let limit_time = last_tx_block + rate_limit;270271					if block_number >= limit_time {272						SponsorBasket::<T>::insert(&called_contract, who, block_number);273						return Some(called_contract);274					}275				}276			}277			None278		}279	}280}