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

difftreelog

source

pallets/evm-contract-helpers/src/lib.rs12.2 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};22use evm_coder::AbiCoder;23pub use pallet::*;24pub use eth::*;25use scale_info::TypeInfo;26use frame_support::storage::bounded_btree_map::BoundedBTreeMap;27pub mod eth;2829/// Maximum number of methods per contract that could have fee limit30pub const MAX_FEE_LIMITED_METHODS: u32 = 5;3132#[frame_support::pallet]33pub mod pallet {34	pub use super::*;35	use crate::eth::ContractHelpersEvents;36	use frame_support::pallet_prelude::*;37	use pallet_evm_coder_substrate::DispatchResult;38	use sp_core::{H160, U256};39	use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};40	use up_data_structs::SponsorshipState;41	use evm_coder::ToLog;4243	#[pallet::config]44	pub trait Config:45		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config46	{47		/// Overarching event type.48		type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;4950		/// Address, under which magic contract will be available51		#[pallet::constant]52		type ContractAddress: Get<H160>;5354		/// In case of enabled sponsoring, but no sponsoring rate limit set,55		/// this value will be used implicitly56		type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;57	}5859	#[pallet::error]60	pub enum Error<T> {61		/// This method is only executable by contract owner62		NoPermission,6364		/// No pending sponsor for contract.65		NoPendingSponsor,6667		/// Number of methods that sponsored limit is defined for exceeds maximum.68		TooManyMethodsHaveSponsoredLimit,69	}7071	#[pallet::pallet]72	#[pallet::generate_store(pub(super) trait Store)]73	pub struct Pallet<T>(_);7475	/// Store owner for contract.76	///77	/// * **Key** - contract address.78	/// * **Value** - owner for contract.79	#[pallet::storage]80	pub(super) type Owner<T: Config> =81		StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8283	#[pallet::storage]84	#[deprecated]85	pub(super) type SelfSponsoring<T: Config> =86		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8788	/// Store for contract sponsorship state.89	///90	/// * **Key** - contract address.91	/// * **Value** - sponsorship state.92	#[pallet::storage]93	pub(super) type Sponsoring<T: Config> = StorageMap<94		Hasher = Twox64Concat,95		Key = H160,96		Value = SponsorshipState<T::CrossAccountId>,97		QueryKind = ValueQuery,98	>;99100	/// Store for sponsoring mode.101	///102	/// ### Usage103	/// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).104	///105	/// * **Key** - contract address.106	/// * **Value** - [`sponsoring mode`](SponsoringModeT).107	#[pallet::storage]108	pub(super) type SponsoringMode<T: Config> =109		StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;110111	/// Storage for sponsoring rate limit in blocks.112	///113	/// * **Key** - contract address.114	/// * **Value** - amount of sponsored blocks.115	#[pallet::storage]116	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<117		Hasher = Twox128,118		Key = H160,119		Value = T::BlockNumber,120		QueryKind = ValueQuery,121		OnEmpty = T::DefaultSponsoringRateLimit,122	>;123124	/// Storage for last sponsored block.125	///126	/// * **Key1** - contract address.127	/// * **Key2** - sponsored user address.128	/// * **Value** - last sponsored block number.129	#[pallet::storage]130	pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<131		Hasher = Twox128,132		Key = H160,133		Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,134		QueryKind = ValueQuery,135	>;136137	#[pallet::storage]138	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<139		Hasher1 = Twox128,140		Key1 = H160,141		Hasher2 = Twox128,142		Key2 = H160,143		Value = T::BlockNumber,144		QueryKind = OptionQuery,145	>;146147	/// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.148	///149	/// ### Usage150	/// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.151	///152	/// * **Key** - contract address.153	/// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.154	#[pallet::storage]155	pub(super) type AllowlistEnabled<T: Config> =156		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;157158	/// Storage for users that allowed for sponsorship.159	///160	/// ### Usage161	/// Prefer to delete record from storage if user no more allowed for sponsorship.162	///163	/// * **Key1** - contract address.164	/// * **Key2** - user that allowed for sponsorship.165	/// * **Value** - allowance for sponsorship.166	#[pallet::storage]167	pub(super) type Allowlist<T: Config> = StorageDoubleMap<168		Hasher1 = Twox128,169		Key1 = H160,170		Hasher2 = Twox128,171		Key2 = H160,172		Value = bool,173		QueryKind = ValueQuery,174	>;175176	#[pallet::event]177	#[pallet::generate_deposit(fn deposit_event)]178	pub enum Event<T: Config> {179		/// Contract sponsor was set.180		ContractSponsorSet(181			/// Contract address of the affected collection.182			H160,183			/// New sponsor address.184			T::AccountId,185		),186187		/// New sponsor was confirm.188		ContractSponsorshipConfirmed(189			/// Contract address of the affected collection.190			H160,191			/// New sponsor address.192			T::AccountId,193		),194195		/// Collection sponsor was removed.196		ContractSponsorRemoved(197			/// Contract address of the affected collection.198			H160,199		),200	}201202	impl<T: Config> Pallet<T> {203		/// Get contract owner.204		pub fn contract_owner(contract: H160) -> H160 {205			<Owner<T>>::get(contract)206		}207208		/// Set `sponsor` for `contract`.209		///210		/// `sender` must be owner of contract.211		pub fn set_sponsor(212			sender: &T::CrossAccountId,213			contract: H160,214			sponsor: &T::CrossAccountId,215		) -> DispatchResult {216			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;217			Sponsoring::<T>::insert(218				contract,219				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),220			);221222			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(223				contract,224				sponsor.as_sub().clone(),225			));226			<PalletEvm<T>>::deposit_log(227				ContractHelpersEvents::ContractSponsorSet {228					contract_address: contract,229					sponsor: *sponsor.as_eth(),230				}231				.to_log(contract),232			);233			Ok(())234		}235236		/// Force set `sponsor` for `contract`.237		///238		/// Differs from `set_sponsor` in that confirmation239		/// from the sponsor is not required.240		pub fn force_set_sponsor(241			contract_address: H160,242			sponsor: &T::CrossAccountId,243		) -> DispatchResult {244			Sponsoring::<T>::insert(245				contract_address,246				SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),247			);248249			let eth_sponsor = *sponsor.as_eth();250			let sub_sponsor = sponsor.as_sub().clone();251252			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(253				contract_address,254				sub_sponsor.clone(),255			));256			<PalletEvm<T>>::deposit_log(257				ContractHelpersEvents::ContractSponsorSet {258					contract_address,259					sponsor: eth_sponsor,260				}261				.to_log(contract_address),262			);263264			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(265				contract_address,266				sub_sponsor,267			));268			<PalletEvm<T>>::deposit_log(269				ContractHelpersEvents::ContractSponsorshipConfirmed {270					contract_address,271					sponsor: eth_sponsor,272				}273				.to_log(contract_address),274			);275276			Ok(())277		}278279		/// Remove sponsor for `contract`.280		///281		/// `sender` must be owner of contract.282		pub fn remove_sponsor(283			sender: &T::CrossAccountId,284			contract_address: H160,285		) -> DispatchResult {286			Self::ensure_owner(contract_address, *sender.as_eth())?;287			Self::force_remove_sponsor(contract_address)288		}289290		/// Force remove `sponsor` for `contract`.291		///292		/// Differs from `remove_sponsor` in that293		/// it doesn't require consent from the `owner` of the contract.294		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {295			Sponsoring::<T>::remove(contract_address);296297			Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));298			<PalletEvm<T>>::deposit_log(299				ContractHelpersEvents::ContractSponsorRemoved { contract_address }300					.to_log(contract_address),301			);302303			Ok(())304		}305306		/// Confirm sponsorship.307		///308		/// `sender` must be same that set via [`set_sponsor`].309		pub fn confirm_sponsorship(310			sender: &T::CrossAccountId,311			contract_address: H160,312		) -> DispatchResult {313			match Sponsoring::<T>::get(contract_address) {314				SponsorshipState::Unconfirmed(sponsor) => {315					ensure!(sponsor == *sender, Error::<T>::NoPermission);316					let eth_sponsor = *sponsor.as_eth();317					let sub_sponsor = sponsor.as_sub().clone();318					Sponsoring::<T>::insert(319						contract_address,320						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),321					);322323					<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(324						contract_address,325						sub_sponsor,326					));327					<PalletEvm<T>>::deposit_log(328						ContractHelpersEvents::ContractSponsorshipConfirmed {329							contract_address,330							sponsor: eth_sponsor,331						}332						.to_log(contract_address),333					);334335					Ok(())336				}337				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {338					Err(Error::<T>::NoPendingSponsor.into())339				}340			}341		}342343		/// Get sponsor.344		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {345			match Sponsoring::<T>::get(contract) {346				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,347				SponsorshipState::Confirmed(sponsor) => Some(sponsor),348			}349		}350351		/// Get current sponsoring mode, performing lazy migration from legacy storage352		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {353			<SponsoringMode<T>>::get(contract)354				.or_else(|| {355					#[allow(deprecated)]356					<SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)357				})358				.unwrap_or_default()359		}360361		/// Reconfigure contract sponsoring mode362		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {363			if mode == SponsoringModeT::Disabled {364				<SponsoringMode<T>>::remove(contract);365			} else {366				<SponsoringMode<T>>::insert(contract, mode);367			}368			#[allow(deprecated)]369			<SelfSponsoring<T>>::remove(contract)370		}371372		/// Set duration between two sponsored contract calls373		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {374			<SponsoringRateLimit<T>>::insert(contract, rate_limit);375		}376377		/// Set maximum for gas limit of transaction378		pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {379			<SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {380				limits_map381					.try_insert(0xffffffff, fee_limit)382					.map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)383			})?;384			Ok(())385		}386387		/// Is user added to allowlist, or he is owner of specified contract388		pub fn allowed(contract: H160, user: H160) -> bool {389			<Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user390		}391392		/// Toggle contract allowlist access393		pub fn toggle_allowlist(contract: H160, enabled: bool) {394			<AllowlistEnabled<T>>::insert(contract, enabled)395		}396397		/// Toggle user presence in contract's allowlist398		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {399			<Allowlist<T>>::insert(contract, user, allowed);400		}401402		/// Throw error if user is not allowed to reconfigure target contract403		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {404			ensure!(<Owner<T>>::get(&contract) == user, Error::<T>::NoPermission);405			Ok(())406		}407	}408}409410/// Available contract sponsoring modes411#[derive(412	Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,413)]414#[repr(u8)]415pub enum SponsoringModeT {416	/// Sponsoring is disabled417	#[default]418	Disabled,419	/// Only users from allowlist will be sponsored420	Allowlisted,421	/// All users will be sponsored422	Generous,423}