git.delta.rocks / unique-network / refs/commits / 11a57bd3fcf7

difftreelog

source

pallets/evm-contract-helpers/src/lib.rs11.6 KiBsourcehistory
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![doc = include_str!("../README.md")]18#![cfg_attr(not(feature = "std"), no_std)]19#![warn(missing_docs)]2021use codec::{Decode, Encode, MaxEncodedLen};22pub use pallet::*;23pub use eth::*;24use scale_info::TypeInfo;25pub mod eth;2627#[frame_support::pallet]28pub mod pallet {29	pub use super::*;30	use crate::eth::ContractHelpersEvents;31	use frame_support::pallet_prelude::*;32	use pallet_evm_coder_substrate::DispatchResult;33	use sp_core::H160;34	use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};35	use up_data_structs::SponsorshipState;36	use evm_coder::ToLog;3738	#[pallet::config]39	pub trait Config:40		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config41	{42		/// Overarching event type.43		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;4445		/// Address, under which magic contract will be available46		type ContractAddress: Get<H160>;4748		/// In case of enabled sponsoring, but no sponsoring rate limit set,49		/// this value will be used implicitly50		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;51	}5253	#[pallet::error]54	pub enum Error<T> {55		/// This method is only executable by contract owner56		NoPermission,5758		/// No pending sponsor for contract.59		NoPendingSponsor,60	}6162	#[pallet::pallet]63	#[pallet::generate_store(pub(super) trait Store)]64	pub struct Pallet<T>(_);6566	/// Store owner for contract.67	///68	/// * **Key** - contract address.69	/// * **Value** - owner for contract.70	#[pallet::storage]71	pub(super) type Owner<T: Config> =72		StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;7374	#[pallet::storage]75	#[deprecated]76	pub(super) type SelfSponsoring<T: Config> =77		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;7879	/// Store for contract sponsorship state.80	///81	/// * **Key** - contract address.82	/// * **Value** - sponsorship state.83	#[pallet::storage]84	pub(super) type Sponsoring<T: Config> = StorageMap<85		Hasher = Twox64Concat,86		Key = H160,87		Value = SponsorshipState<T::CrossAccountId>,88		QueryKind = ValueQuery,89	>;9091	/// Store for sponsoring mode.92	///93	/// ### Usage94	/// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).95	///96	/// * **Key** - contract address.97	/// * **Value** - [`sponsoring mode`](SponsoringModeT).98	#[pallet::storage]99	pub(super) type SponsoringMode<T: Config> =100		StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;101102	/// Storage for sponsoring rate limit in blocks.103	///104	/// * **Key** - contract address.105	/// * **Value** - amount of sponsored blocks.106	#[pallet::storage]107	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<108		Hasher = Twox128,109		Key = H160,110		Value = T::BlockNumber,111		QueryKind = ValueQuery,112		OnEmpty = T::DefaultSponsoringRateLimit,113	>;114115	/// Storage for last sponsored block.116	///117	/// * **Key1** - contract address.118	/// * **Key2** - sponsored user address.119	/// * **Value** - last sponsored block number.120	#[pallet::storage]121	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<122		Hasher1 = Twox128,123		Key1 = H160,124		Hasher2 = Twox128,125		Key2 = H160,126		Value = T::BlockNumber,127		QueryKind = OptionQuery,128	>;129130	/// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.131	///132	/// ### Usage133	/// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.134	///135	/// * **Key** - contract address.136	/// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.137	#[pallet::storage]138	pub(super) type AllowlistEnabled<T: Config> =139		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;140141	/// Storage for users that allowed for sponsorship.142	///143	/// ### Usage144	/// Prefer to delete record from storage if user no more allowed for sponsorship.145	///146	/// * **Key1** - contract address.147	/// * **Key2** - user that allowed for sponsorship.148	/// * **Value** - allowance for sponsorship.149	#[pallet::storage]150	pub(super) type Allowlist<T: Config> = StorageDoubleMap<151		Hasher1 = Twox128,152		Key1 = H160,153		Hasher2 = Twox128,154		Key2 = H160,155		Value = bool,156		QueryKind = ValueQuery,157	>;158159	#[pallet::event]160	#[pallet::generate_deposit(pub fn deposit_event)]161	pub enum Event<T: Config> {162		/// Contract sponsor was set.163		ContractSponsorSet(164			/// Contract address of the affected collection.165			H160,166			/// New sponsor address.167			T::AccountId,168		),169170		/// New sponsor was confirm.171		ContractSponsorshipConfirmed(172			/// Contract address of the affected collection.173			H160,174			/// New sponsor address.175			T::AccountId,176		),177178		/// Collection sponsor was removed.179		ContractSponsorRemoved(180			/// Contract address of the affected collection.181			H160,182		),183	}184185	impl<T: Config> Pallet<T> {186		/// Get contract owner.187		pub fn contract_owner(contract: H160) -> H160 {188			<Owner<T>>::get(contract)189		}190191		/// Set `sponsor` for `contract`.192		///193		/// `sender` must be owner of contract.194		pub fn set_sponsor(195			sender: &T::CrossAccountId,196			contract: H160,197			sponsor: &T::CrossAccountId,198		) -> DispatchResult {199			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;200			Sponsoring::<T>::insert(201				contract,202				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),203			);204205			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(206				contract,207				sponsor.as_sub().clone(),208			));209			<PalletEvm<T>>::deposit_log(210				ContractHelpersEvents::ContractSponsorSet {211					contract_address: contract,212					sponsor: *sponsor.as_eth(),213				}214				.to_log(contract),215			);216			Ok(())217		}218219		/// Force set `sponsor` for `contract`.220		///221		/// Differs from `set_sponsor` in that confirmation222		/// from the sponsor is not required.223		pub fn force_set_sponsor(224			contract_address: H160,225			sponsor: &T::CrossAccountId,226		) -> DispatchResult {227			Sponsoring::<T>::insert(228				contract_address,229				SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),230			);231232			let eth_sponsor = *sponsor.as_eth();233			let sub_sponsor = sponsor.as_sub().clone();234235			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(236				contract_address,237				sub_sponsor.clone(),238			));239			<PalletEvm<T>>::deposit_log(240				ContractHelpersEvents::ContractSponsorSet {241					contract_address,242					sponsor: eth_sponsor,243				}244				.to_log(contract_address),245			);246247			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(248				contract_address,249				sub_sponsor,250			));251			<PalletEvm<T>>::deposit_log(252				ContractHelpersEvents::ContractSponsorshipConfirmed {253					contract_address,254					sponsor: eth_sponsor,255				}256				.to_log(contract_address),257			);258259			Ok(())260		}261262		/// Remove sponsor for `contract`.263		///264		/// `sender` must be owner of contract.265		pub fn remove_sponsor(266			sender: &T::CrossAccountId,267			contract_address: H160,268		) -> DispatchResult {269			Self::ensure_owner(contract_address, *sender.as_eth())?;270			Self::force_remove_sponsor(contract_address)271		}272273		/// Force remove `sponsor` for `contract`.274		///275		/// Differs from `remove_sponsor` in that276		/// it doesn't require consent from the `owner` of the contract.277		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {278			Sponsoring::<T>::remove(contract_address);279280			Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));281			<PalletEvm<T>>::deposit_log(282				ContractHelpersEvents::ContractSponsorRemoved { contract_address }283					.to_log(contract_address),284			);285286			Ok(())287		}288289		/// Confirm sponsorship.290		///291		/// `sender` must be same that set via [`set_sponsor`].292		pub fn confirm_sponsorship(293			sender: &T::CrossAccountId,294			contract_address: H160,295		) -> DispatchResult {296			match Sponsoring::<T>::get(contract_address) {297				SponsorshipState::Unconfirmed(sponsor) => {298					ensure!(sponsor == *sender, Error::<T>::NoPermission);299					let eth_sponsor = *sponsor.as_eth();300					let sub_sponsor = sponsor.as_sub().clone();301					Sponsoring::<T>::insert(302						contract_address,303						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),304					);305306					<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(307						contract_address,308						sub_sponsor,309					));310					<PalletEvm<T>>::deposit_log(311						ContractHelpersEvents::ContractSponsorshipConfirmed {312							contract_address,313							sponsor: eth_sponsor,314						}315						.to_log(contract_address),316					);317318					Ok(())319				}320				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {321					Err(Error::<T>::NoPendingSponsor.into())322				}323			}324		}325326		/// Get sponsor.327		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {328			match Sponsoring::<T>::get(contract) {329				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,330				SponsorshipState::Confirmed(sponsor) => Some(sponsor),331			}332		}333334		/// Get current sponsoring mode, performing lazy migration from legacy storage335		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {336			<SponsoringMode<T>>::get(contract)337				.or_else(|| {338					<SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)339				})340				.unwrap_or_default()341		}342343		/// Reconfigure contract sponsoring mode344		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {345			if mode == SponsoringModeT::Disabled {346				<SponsoringMode<T>>::remove(contract);347			} else {348				<SponsoringMode<T>>::insert(contract, mode);349			}350			<SelfSponsoring<T>>::remove(contract)351		}352353		/// Set duration between two sponsored contract calls354		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {355			<SponsoringRateLimit<T>>::insert(contract, rate_limit);356		}357358		/// Is user added to allowlist, or he is owner of specified contract359		pub fn allowed(contract: H160, user: H160) -> bool {360			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user361		}362363		/// Toggle contract allowlist access364		pub fn toggle_allowlist(contract: H160, enabled: bool) {365			<AllowlistEnabled<T>>::insert(contract, enabled)366		}367368		/// Toggle user presence in contract's allowlist369		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {370			<Allowlist<T>>::insert(contract, user, allowed);371		}372373		/// Throw error if user is not allowed to reconfigure target contract374		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {375			ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);376			Ok(())377		}378	}379}380381/// Available contract sponsoring modes382#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]383pub enum SponsoringModeT {384	/// Sponsoring is disabled385	#[default]386	Disabled,387	/// Only users from allowlist will be sponsored388	Allowlisted,389	/// All users will be sponsored390	Generous,391}392393impl SponsoringModeT {394	fn from_eth(v: u8) -> Option<Self> {395		Some(match v {396			0 => Self::Disabled,397			1 => Self::Allowlisted,398			2 => Self::Generous,399			_ => return None,400		})401	}402	fn to_eth(self) -> u8 {403		match self {404			SponsoringModeT::Disabled => 0,405			SponsoringModeT::Allowlisted => 1,406			SponsoringModeT::Generous => 2,407		}408	}409}