git.delta.rocks / unique-network / refs/commits / 7e9e8858b433

difftreelog

source

pallets/evm-contract-helpers/src/lib.rs11.9 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, U256};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		/// In case of enabled sponsoring, but no sponsoring fee limit set,52		/// this value will be used implicitly53		type DefaultSponsoringFeeLimit: Get<U256>;54	}5556	#[pallet::error]57	pub enum Error<T> {58		/// This method is only executable by contract owner59		NoPermission,6061		/// No pending sponsor for contract.62		NoPendingSponsor,63	}6465	#[pallet::pallet]66	#[pallet::generate_store(pub(super) trait Store)]67	pub struct Pallet<T>(_);6869	/// Store owner for contract.70	///71	/// * **Key** - contract address.72	/// * **Value** - owner for contract.73	#[pallet::storage]74	pub(super) type Owner<T: Config> =75		StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;7677	#[pallet::storage]78	#[deprecated]79	pub(super) type SelfSponsoring<T: Config> =80		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8182	/// Store for contract sponsorship state.83	///84	/// * **Key** - contract address.85	/// * **Value** - sponsorship state.86	#[pallet::storage]87	pub(super) type Sponsoring<T: Config> = StorageMap<88		Hasher = Twox64Concat,89		Key = H160,90		Value = SponsorshipState<T::CrossAccountId>,91		QueryKind = ValueQuery,92	>;9394	/// Store for sponsoring mode.95	///96	/// ### Usage97	/// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).98	///99	/// * **Key** - contract address.100	/// * **Value** - [`sponsoring mode`](SponsoringModeT).101	#[pallet::storage]102	pub(super) type SponsoringMode<T: Config> =103		StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;104105	/// Storage for sponsoring rate limit in blocks.106	///107	/// * **Key** - contract address.108	/// * **Value** - amount of sponsored blocks.109	#[pallet::storage]110	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<111		Hasher = Twox128,112		Key = H160,113		Value = T::BlockNumber,114		QueryKind = ValueQuery,115		OnEmpty = T::DefaultSponsoringRateLimit,116	>;117118	/// Storage for last sponsored block.119	///120	/// * **Key1** - contract address.121	/// * **Key2** - sponsored user address.122	/// * **Value** - last sponsored block number.123	#[pallet::storage]124	pub(super) type SponsoringFeeLimit<T: Config> = StorageDoubleMap<125		Hasher1 = Twox128,126		Key1 = H160,127		Hasher2 = Blake2_128Concat,128		Key2 = u32,129		Value = U256,130		QueryKind = ValueQuery,131		OnEmpty = T::DefaultSponsoringFeeLimit,132	>;133134	#[pallet::storage]135	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<136		Hasher1 = Twox128,137		Key1 = H160,138		Hasher2 = Twox128,139		Key2 = H160,140		Value = T::BlockNumber,141		QueryKind = OptionQuery,142	>;143144	/// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.145	///146	/// ### Usage147	/// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.148	///149	/// * **Key** - contract address.150	/// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.151	#[pallet::storage]152	pub(super) type AllowlistEnabled<T: Config> =153		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;154155	/// Storage for users that allowed for sponsorship.156	///157	/// ### Usage158	/// Prefer to delete record from storage if user no more allowed for sponsorship.159	///160	/// * **Key1** - contract address.161	/// * **Key2** - user that allowed for sponsorship.162	/// * **Value** - allowance for sponsorship.163	#[pallet::storage]164	pub(super) type Allowlist<T: Config> = StorageDoubleMap<165		Hasher1 = Twox128,166		Key1 = H160,167		Hasher2 = Twox128,168		Key2 = H160,169		Value = bool,170		QueryKind = ValueQuery,171	>;172173	#[pallet::event]174	#[pallet::generate_deposit(pub fn deposit_event)]175	pub enum Event<T: Config> {176		/// Contract sponsor was set.177		ContractSponsorSet(178			/// Contract address of the affected collection.179			H160,180			/// New sponsor address.181			T::AccountId,182		),183184		/// New sponsor was confirm.185		ContractSponsorshipConfirmed(186			/// Contract address of the affected collection.187			H160,188			/// New sponsor address.189			T::AccountId,190		),191192		/// Collection sponsor was removed.193		ContractSponsorRemoved(194			/// Contract address of the affected collection.195			H160,196		),197	}198199	impl<T: Config> Pallet<T> {200		/// Get contract owner.201		pub fn contract_owner(contract: H160) -> H160 {202			<Owner<T>>::get(contract)203		}204205		/// Set `sponsor` for `contract`.206		///207		/// `sender` must be owner of contract.208		pub fn set_sponsor(209			sender: &T::CrossAccountId,210			contract: H160,211			sponsor: &T::CrossAccountId,212		) -> DispatchResult {213			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;214			Sponsoring::<T>::insert(215				contract,216				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),217			);218219			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(220				contract,221				sponsor.as_sub().clone(),222			));223			<PalletEvm<T>>::deposit_log(224				ContractHelpersEvents::ContractSponsorSet {225					contract_address: contract,226					sponsor: *sponsor.as_eth(),227				}228				.to_log(contract),229			);230			Ok(())231		}232233		/// TO-DO234		///235		///236		pub fn force_set_sponsor(237			contract_address: H160,238			sponsor: &T::CrossAccountId,239		) -> DispatchResult {240			Sponsoring::<T>::insert(241				contract_address,242				SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),243			);244245			let eth_sponsor = *sponsor.as_eth();246			let sub_sponsor = sponsor.as_sub().clone();247248			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(249				contract_address,250				sub_sponsor.clone(),251			));252			<PalletEvm<T>>::deposit_log(253				ContractHelpersEvents::ContractSponsorSet {254					contract_address,255					sponsor: eth_sponsor,256				}257				.to_log(contract_address),258			);259260			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(261				contract_address,262				sub_sponsor,263			));264			<PalletEvm<T>>::deposit_log(265				ContractHelpersEvents::ContractSponsorshipConfirmed {266					contract_address,267					sponsor: eth_sponsor,268				}269				.to_log(contract_address),270			);271272			Ok(())273		}274275		/// Remove sponsor for `contract`.276		///277		/// `sender` must be owner of contract.278		pub fn remove_sponsor(279			sender: &T::CrossAccountId,280			contract_address: H160,281		) -> DispatchResult {282			Self::ensure_owner(contract_address, *sender.as_eth())?;283			Self::force_remove_sponsor(contract_address)284		}285286		/// TO-DO287		///288		///289		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {290			Sponsoring::<T>::remove(contract_address);291292			Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));293			<PalletEvm<T>>::deposit_log(294				ContractHelpersEvents::ContractSponsorRemoved { contract_address }295					.to_log(contract_address),296			);297298			Ok(())299		}300301		/// Confirm sponsorship.302		///303		/// `sender` must be same that set via [`set_sponsor`].304		pub fn confirm_sponsorship(305			sender: &T::CrossAccountId,306			contract_address: H160,307		) -> DispatchResult {308			match Sponsoring::<T>::get(contract_address) {309				SponsorshipState::Unconfirmed(sponsor) => {310					ensure!(sponsor == *sender, Error::<T>::NoPermission);311					let eth_sponsor = *sponsor.as_eth();312					let sub_sponsor = sponsor.as_sub().clone();313					Sponsoring::<T>::insert(314						contract_address,315						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),316					);317318					<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(319						contract_address,320						sub_sponsor,321					));322					<PalletEvm<T>>::deposit_log(323						ContractHelpersEvents::ContractSponsorshipConfirmed {324							contract_address,325							sponsor: eth_sponsor,326						}327						.to_log(contract_address),328					);329330					Ok(())331				}332				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {333					Err(Error::<T>::NoPendingSponsor.into())334				}335			}336		}337338		/// Get sponsor.339		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {340			match Sponsoring::<T>::get(contract) {341				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,342				SponsorshipState::Confirmed(sponsor) => Some(sponsor),343			}344		}345346		/// Get current sponsoring mode, performing lazy migration from legacy storage347		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {348			<SponsoringMode<T>>::get(contract)349				.or_else(|| {350					<SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)351				})352				.unwrap_or_default()353		}354355		/// Reconfigure contract sponsoring mode356		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {357			if mode == SponsoringModeT::Disabled {358				<SponsoringMode<T>>::remove(contract);359			} else {360				<SponsoringMode<T>>::insert(contract, mode);361			}362			<SelfSponsoring<T>>::remove(contract)363		}364365		/// Set duration between two sponsored contract calls366		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {367			<SponsoringRateLimit<T>>::insert(contract, rate_limit);368		}369370		/// Set maximum for gas limit of transaction371		pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) {372			<SponsoringFeeLimit<T>>::insert(contract, 0xffffffff, fee_limit);373		}374375		/// Is user added to allowlist, or he is owner of specified contract376		pub fn allowed(contract: H160, user: H160) -> bool {377			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user378		}379380		/// Toggle contract allowlist access381		pub fn toggle_allowlist(contract: H160, enabled: bool) {382			<AllowlistEnabled<T>>::insert(contract, enabled)383		}384385		/// Toggle user presence in contract's allowlist386		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {387			<Allowlist<T>>::insert(contract, user, allowed);388		}389390		/// Throw error if user is not allowed to reconfigure target contract391		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {392			ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);393			Ok(())394		}395	}396}397398/// Available contract sponsoring modes399#[derive(Encode, Decode, PartialEq, TypeInfo, MaxEncodedLen, Default)]400pub enum SponsoringModeT {401	/// Sponsoring is disabled402	#[default]403	Disabled,404	/// Only users from allowlist will be sponsored405	Allowlisted,406	/// All users will be sponsored407	Generous,408}409410impl SponsoringModeT {411	fn from_eth(v: u8) -> Option<Self> {412		Some(match v {413			0 => Self::Disabled,414			1 => Self::Allowlisted,415			2 => Self::Generous,416			_ => return None,417		})418	}419	fn to_eth(self) -> u8 {420		match self {421			SponsoringModeT::Disabled => 0,422			SponsoringModeT::Allowlisted => 1,423			SponsoringModeT::Generous => 2,424		}425	}426}