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

difftreelog

source

pallets/evm-contract-helpers/src/lib.rs13.0 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 frame_support::{pallet_prelude::*, sp_runtime::DispatchResult};36	use frame_system::{pallet_prelude::OriginFor, ensure_root};37	use sp_core::{H160, U256};38	use sp_std::vec::Vec;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	pub struct Pallet<T>(_);7374	/// Store owner for contract.75	///76	/// * **Key** - contract address.77	/// * **Value** - owner for contract.78	#[pallet::storage]79	pub(super) type Owner<T: Config> =80		StorageMap<Hasher = Twox128, Key = H160, Value = H160, QueryKind = ValueQuery>;8182	/// Deprecated: this storage is deprecated83	#[pallet::storage]84	type SelfSponsoring<T: Config> =85		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;8687	/// Store for contract sponsorship state.88	///89	/// * **Key** - contract address.90	/// * **Value** - sponsorship state.91	#[pallet::storage]92	pub(super) type Sponsoring<T: Config> = StorageMap<93		Hasher = Twox64Concat,94		Key = H160,95		Value = SponsorshipState<T::CrossAccountId>,96		QueryKind = ValueQuery,97	>;9899	/// Store for sponsoring mode.100	///101	/// ### Usage102	/// Prefer to delete collection from storage if mode chaged to [`Disabled`](SponsoringModeT::Disabled).103	///104	/// * **Key** - contract address.105	/// * **Value** - [`sponsoring mode`](SponsoringModeT).106	#[pallet::storage]107	pub(super) type SponsoringMode<T: Config> =108		StorageMap<Hasher = Twox128, Key = H160, Value = SponsoringModeT, QueryKind = OptionQuery>;109110	/// Storage for sponsoring rate limit in blocks.111	///112	/// * **Key** - contract address.113	/// * **Value** - amount of sponsored blocks.114	#[pallet::storage]115	pub(super) type SponsoringRateLimit<T: Config> = StorageMap<116		Hasher = Twox128,117		Key = H160,118		Value = T::BlockNumber,119		QueryKind = ValueQuery,120		OnEmpty = T::DefaultSponsoringRateLimit,121	>;122123	/// Storage for last sponsored block.124	///125	/// * **Key1** - contract address.126	/// * **Key2** - sponsored user address.127	/// * **Value** - last sponsored block number.128	#[pallet::storage]129	pub(super) type SponsoringFeeLimit<T: Config> = StorageMap<130		Hasher = Twox128,131		Key = H160,132		Value = BoundedBTreeMap<u32, U256, ConstU32<MAX_FEE_LIMITED_METHODS>>,133		QueryKind = ValueQuery,134	>;135136	#[pallet::storage]137	pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<138		Hasher1 = Twox128,139		Key1 = H160,140		Hasher2 = Twox128,141		Key2 = H160,142		Value = T::BlockNumber,143		QueryKind = OptionQuery,144	>;145146	/// Storege for contracts with [`Allowlisted`](SponsoringModeT::Allowlisted) sponsoring mode.147	///148	/// ### Usage149	/// Prefer to delete collection from storage if mode chaged to non `Allowlisted`, than set **Value** to **false**.150	///151	/// * **Key** - contract address.152	/// * **Value** - is contract in [`Allowlisted`](SponsoringModeT::Allowlisted) mode.153	#[pallet::storage]154	pub(super) type AllowlistEnabled<T: Config> =155		StorageMap<Hasher = Twox128, Key = H160, Value = bool, QueryKind = ValueQuery>;156157	/// Storage for users that allowed for sponsorship.158	///159	/// ### Usage160	/// Prefer to delete record from storage if user no more allowed for sponsorship.161	///162	/// * **Key1** - contract address.163	/// * **Key2** - user that allowed for sponsorship.164	/// * **Value** - allowance for sponsorship.165	#[pallet::storage]166	pub(super) type Allowlist<T: Config> = StorageDoubleMap<167		Hasher1 = Twox128,168		Key1 = H160,169		Hasher2 = Twox128,170		Key2 = H160,171		Value = bool,172		QueryKind = ValueQuery,173	>;174175	#[pallet::event]176	#[pallet::generate_deposit(fn deposit_event)]177	pub enum Event<T: Config> {178		/// Contract sponsor was set.179		ContractSponsorSet(180			/// Contract address of the affected collection.181			H160,182			/// New sponsor address.183			T::AccountId,184		),185186		/// New sponsor was confirm.187		ContractSponsorshipConfirmed(188			/// Contract address of the affected collection.189			H160,190			/// New sponsor address.191			T::AccountId,192		),193194		/// Collection sponsor was removed.195		ContractSponsorRemoved(196			/// Contract address of the affected collection.197			H160,198		),199	}200201	#[pallet::call]202	impl<T: Config> Pallet<T> {203		/// Migrate contract to use `SponsoringMode` storage instead of `SelfSponsoring`204		#[pallet::call_index(0)]205		#[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(206			addresses.len() as u64, addresses.len() as u64 * 2207		))]208		pub fn migrate_from_self_sponsoring(209			origin: OriginFor<T>,210			addresses: Vec<H160>,211		) -> DispatchResult {212			ensure_root(origin)?;213			for address in addresses {214				if <SelfSponsoring<T>>::get(address) {215					<SponsoringMode<T>>::set(address, Some(SponsoringModeT::Allowlisted));216					<SelfSponsoring<T>>::remove(address);217				}218			}219			Ok(())220		}221	}222223	impl<T: Config> Pallet<T> {224		/// Get contract owner.225		pub fn contract_owner(contract: H160) -> H160 {226			<Owner<T>>::get(contract)227		}228229		/// Set `sponsor` for `contract`.230		///231		/// `sender` must be owner of contract.232		pub fn set_sponsor(233			sender: &T::CrossAccountId,234			contract: H160,235			sponsor: &T::CrossAccountId,236		) -> DispatchResult {237			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;238			Sponsoring::<T>::insert(239				contract,240				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),241			);242243			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(244				contract,245				sponsor.as_sub().clone(),246			));247			<PalletEvm<T>>::deposit_log(248				ContractHelpersEvents::ContractSponsorSet {249					contract_address: contract,250					sponsor: *sponsor.as_eth(),251				}252				.to_log(contract),253			);254			Ok(())255		}256257		/// Force set `sponsor` for `contract`.258		///259		/// Differs from `set_sponsor` in that confirmation260		/// from the sponsor is not required.261		pub fn force_set_sponsor(262			contract_address: H160,263			sponsor: &T::CrossAccountId,264		) -> DispatchResult {265			Sponsoring::<T>::insert(266				contract_address,267				SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),268			);269270			let eth_sponsor = *sponsor.as_eth();271			let sub_sponsor = sponsor.as_sub().clone();272273			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(274				contract_address,275				sub_sponsor.clone(),276			));277			<PalletEvm<T>>::deposit_log(278				ContractHelpersEvents::ContractSponsorSet {279					contract_address,280					sponsor: eth_sponsor,281				}282				.to_log(contract_address),283			);284285			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(286				contract_address,287				sub_sponsor,288			));289			<PalletEvm<T>>::deposit_log(290				ContractHelpersEvents::ContractSponsorshipConfirmed {291					contract_address,292					sponsor: eth_sponsor,293				}294				.to_log(contract_address),295			);296297			Ok(())298		}299300		/// Remove sponsor for `contract`.301		///302		/// `sender` must be owner of contract.303		pub fn remove_sponsor(304			sender: &T::CrossAccountId,305			contract_address: H160,306		) -> DispatchResult {307			Self::ensure_owner(contract_address, *sender.as_eth())?;308			Self::force_remove_sponsor(contract_address)309		}310311		/// Force remove `sponsor` for `contract`.312		///313		/// Differs from `remove_sponsor` in that314		/// it doesn't require consent from the `owner` of the contract.315		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {316			Sponsoring::<T>::remove(contract_address);317318			Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));319			<PalletEvm<T>>::deposit_log(320				ContractHelpersEvents::ContractSponsorRemoved { contract_address }321					.to_log(contract_address),322			);323324			Ok(())325		}326327		/// Confirm sponsorship.328		///329		/// `sender` must be same that set via [`set_sponsor`].330		pub fn confirm_sponsorship(331			sender: &T::CrossAccountId,332			contract_address: H160,333		) -> DispatchResult {334			match Sponsoring::<T>::get(contract_address) {335				SponsorshipState::Unconfirmed(sponsor) => {336					ensure!(sponsor == *sender, Error::<T>::NoPermission);337					let eth_sponsor = *sponsor.as_eth();338					let sub_sponsor = sponsor.as_sub().clone();339					Sponsoring::<T>::insert(340						contract_address,341						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),342					);343344					<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(345						contract_address,346						sub_sponsor,347					));348					<PalletEvm<T>>::deposit_log(349						ContractHelpersEvents::ContractSponsorshipConfirmed {350							contract_address,351							sponsor: eth_sponsor,352						}353						.to_log(contract_address),354					);355356					Ok(())357				}358				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {359					Err(Error::<T>::NoPendingSponsor.into())360				}361			}362		}363364		/// Get sponsor.365		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {366			match Sponsoring::<T>::get(contract) {367				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,368				SponsorshipState::Confirmed(sponsor) => Some(sponsor),369			}370		}371372		/// Get current sponsoring mode, performing lazy migration from legacy storage373		/// Deprecated: this method is for deprecated storage374		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {375			<SponsoringMode<T>>::get(contract)376				.or_else(|| {377					#[allow(deprecated)]378					<SelfSponsoring<T>>::get(contract).then_some(SponsoringModeT::Allowlisted)379				})380				.unwrap_or_default()381		}382383		/// Reconfigure contract sponsoring mode384		/// Deprecated: this method is for deprecated storage385		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {386			if mode == SponsoringModeT::Disabled {387				<SponsoringMode<T>>::remove(contract);388			} else {389				<SponsoringMode<T>>::insert(contract, mode);390			}391			#[allow(deprecated)]392			<SelfSponsoring<T>>::remove(contract)393		}394395		/// Set duration between two sponsored contract calls396		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {397			<SponsoringRateLimit<T>>::insert(contract, rate_limit);398		}399400		/// Set maximum for gas limit of transaction401		pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {402			<SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {403				limits_map404					.try_insert(0xffffffff, fee_limit)405					.map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)406			})?;407			Ok(())408		}409410		/// Is user added to allowlist, or he is owner of specified contract411		pub fn allowed(contract: H160, user: H160) -> bool {412			<Allowlist<T>>::get(contract, user) || <Owner<T>>::get(contract) == user413		}414415		/// Toggle contract allowlist access416		pub fn toggle_allowlist(contract: H160, enabled: bool) {417			<AllowlistEnabled<T>>::insert(contract, enabled)418		}419420		/// Toggle user presence in contract's allowlist421		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {422			<Allowlist<T>>::insert(contract, user, allowed);423		}424425		/// Throw error if user is not allowed to reconfigure target contract426		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {427			ensure!(<Owner<T>>::get(contract) == user, Error::<T>::NoPermission);428			Ok(())429		}430	}431}432433/// Available contract sponsoring modes434#[derive(435	Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,436)]437#[repr(u8)]438pub enum SponsoringModeT {439	/// Sponsoring is disabled440	#[default]441	Disabled,442	/// Only users from allowlist will be sponsored443	Allowlisted,444	/// All users will be sponsored445	Generous,446}