git.delta.rocks / unique-network / refs/commits / 68c3995040db

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)]2021pub use eth::*;22use evm_coder::AbiCoder;23use frame_support::storage::bounded_btree_map::BoundedBTreeMap;24pub use pallet::*;25use parity_scale_codec::{Decode, Encode, MaxEncodedLen};26use scale_info::TypeInfo;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	use evm_coder::ToLog;35	use frame_support::{pallet_prelude::*, sp_runtime::DispatchResult};36	use frame_system::{ensure_root, pallet_prelude::*};37	use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};38	use sp_core::{H160, U256};39	use sp_std::vec::Vec;40	use up_data_structs::SponsorshipState;4142	pub use super::*;4344	#[pallet::config]45	pub trait Config:46		frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::Config47	{48		/// Overarching event type.49		type RuntimeEvent: IsType<<Self as frame_system::Config>::RuntimeEvent> + From<Event<Self>>;5051		/// Address, under which magic contract will be available52		#[pallet::constant]53		type ContractAddress: Get<H160>;5455		/// In case of enabled sponsoring, but no sponsoring rate limit set,56		/// this value will be used implicitly57		type DefaultSponsoringRateLimit: Get<BlockNumberFor<Self>>;58	}5960	#[pallet::error]61	pub enum Error<T> {62		/// This method is only executable by contract owner63		NoPermission,6465		/// No pending sponsor for contract.66		NoPendingSponsor,6768		/// Number of methods that sponsored limit is defined for exceeds maximum.69		TooManyMethodsHaveSponsoredLimit,70	}7172	#[pallet::pallet]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	/// Deprecated: this storage is deprecated84	#[pallet::storage]85	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 = BlockNumberFor<T>,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 = BlockNumberFor<T>,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	#[pallet::call]203	impl<T: Config> Pallet<T> {204		/// Migrate contract to use `SponsoringMode` storage instead of `SelfSponsoring`205		#[pallet::call_index(0)]206		#[pallet::weight(<T as frame_system::Config>::DbWeight::get().reads_writes(207			addresses.len() as u64, addresses.len() as u64 * 2208		))]209		pub fn migrate_from_self_sponsoring(210			origin: OriginFor<T>,211			addresses: Vec<H160>,212		) -> DispatchResult {213			ensure_root(origin)?;214			for address in addresses {215				if <SelfSponsoring<T>>::get(address) {216					<SponsoringMode<T>>::set(address, Some(SponsoringModeT::Allowlisted));217					<SelfSponsoring<T>>::remove(address);218				}219			}220			Ok(())221		}222	}223224	impl<T: Config> Pallet<T> {225		/// Get contract owner.226		pub fn contract_owner(contract: H160) -> H160 {227			<Owner<T>>::get(contract)228		}229230		/// Set `sponsor` for `contract`.231		///232		/// `sender` must be owner of contract.233		pub fn set_sponsor(234			sender: &T::CrossAccountId,235			contract: H160,236			sponsor: &T::CrossAccountId,237		) -> DispatchResult {238			Pallet::<T>::ensure_owner(contract, *sender.as_eth())?;239			Sponsoring::<T>::insert(240				contract,241				SponsorshipState::<T::CrossAccountId>::Unconfirmed(sponsor.clone()),242			);243244			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(245				contract,246				sponsor.as_sub().clone(),247			));248			<PalletEvm<T>>::deposit_log(249				ContractHelpersEvents::ContractSponsorSet {250					contract_address: contract,251					sponsor: *sponsor.as_eth(),252				}253				.to_log(contract),254			);255			Ok(())256		}257258		/// Force set `sponsor` for `contract`.259		///260		/// Differs from `set_sponsor` in that confirmation261		/// from the sponsor is not required.262		pub fn force_set_sponsor(263			contract_address: H160,264			sponsor: &T::CrossAccountId,265		) -> DispatchResult {266			Sponsoring::<T>::insert(267				contract_address,268				SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor.clone()),269			);270271			let eth_sponsor = *sponsor.as_eth();272			let sub_sponsor = sponsor.as_sub().clone();273274			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorSet(275				contract_address,276				sub_sponsor.clone(),277			));278			<PalletEvm<T>>::deposit_log(279				ContractHelpersEvents::ContractSponsorSet {280					contract_address,281					sponsor: eth_sponsor,282				}283				.to_log(contract_address),284			);285286			<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(287				contract_address,288				sub_sponsor,289			));290			<PalletEvm<T>>::deposit_log(291				ContractHelpersEvents::ContractSponsorshipConfirmed {292					contract_address,293					sponsor: eth_sponsor,294				}295				.to_log(contract_address),296			);297298			Ok(())299		}300301		/// Remove sponsor for `contract`.302		///303		/// `sender` must be owner of contract.304		pub fn remove_sponsor(305			sender: &T::CrossAccountId,306			contract_address: H160,307		) -> DispatchResult {308			Self::ensure_owner(contract_address, *sender.as_eth())?;309			Self::force_remove_sponsor(contract_address)310		}311312		/// Force remove `sponsor` for `contract`.313		///314		/// Differs from `remove_sponsor` in that315		/// it doesn't require consent from the `owner` of the contract.316		pub fn force_remove_sponsor(contract_address: H160) -> DispatchResult {317			Sponsoring::<T>::remove(contract_address);318319			Self::deposit_event(Event::<T>::ContractSponsorRemoved(contract_address));320			<PalletEvm<T>>::deposit_log(321				ContractHelpersEvents::ContractSponsorRemoved { contract_address }322					.to_log(contract_address),323			);324325			Ok(())326		}327328		/// Confirm sponsorship.329		///330		/// `sender` must be same that set via [`set_sponsor`].331		pub fn confirm_sponsorship(332			sender: &T::CrossAccountId,333			contract_address: H160,334		) -> DispatchResult {335			match Sponsoring::<T>::get(contract_address) {336				SponsorshipState::Unconfirmed(sponsor) => {337					ensure!(sponsor == *sender, Error::<T>::NoPermission);338					let eth_sponsor = *sponsor.as_eth();339					let sub_sponsor = sponsor.as_sub().clone();340					Sponsoring::<T>::insert(341						contract_address,342						SponsorshipState::<T::CrossAccountId>::Confirmed(sponsor),343					);344345					<Pallet<T>>::deposit_event(Event::<T>::ContractSponsorshipConfirmed(346						contract_address,347						sub_sponsor,348					));349					<PalletEvm<T>>::deposit_log(350						ContractHelpersEvents::ContractSponsorshipConfirmed {351							contract_address,352							sponsor: eth_sponsor,353						}354						.to_log(contract_address),355					);356357					Ok(())358				}359				SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => {360					Err(Error::<T>::NoPendingSponsor.into())361				}362			}363		}364365		/// Get sponsor.366		pub fn get_sponsor(contract: H160) -> Option<T::CrossAccountId> {367			match Sponsoring::<T>::get(contract) {368				SponsorshipState::Disabled | SponsorshipState::Unconfirmed(_) => None,369				SponsorshipState::Confirmed(sponsor) => Some(sponsor),370			}371		}372373		/// Get current sponsoring mode, performing lazy migration from legacy storage374		/// Deprecated: this method is for deprecated storage375		pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {376			<SponsoringMode<T>>::get(contract)377				.or_else(|| {378					#[allow(deprecated)]379					<SelfSponsoring<T>>::get(contract).then_some(SponsoringModeT::Allowlisted)380				})381				.unwrap_or_default()382		}383384		/// Reconfigure contract sponsoring mode385		/// Deprecated: this method is for deprecated storage386		pub fn set_sponsoring_mode(contract: H160, mode: SponsoringModeT) {387			if mode == SponsoringModeT::Disabled {388				<SponsoringMode<T>>::remove(contract);389			} else {390				<SponsoringMode<T>>::insert(contract, mode);391			}392			#[allow(deprecated)]393			<SelfSponsoring<T>>::remove(contract)394		}395396		/// Set duration between two sponsored contract calls397		pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: BlockNumberFor<T>) {398			<SponsoringRateLimit<T>>::insert(contract, rate_limit);399		}400401		/// Set maximum for gas limit of transaction402		pub fn set_sponsoring_fee_limit(contract: H160, fee_limit: U256) -> DispatchResult {403			<SponsoringFeeLimit<T>>::try_mutate(contract, |limits_map| {404				limits_map405					.try_insert(0xffffffff, fee_limit)406					.map_err(|_| <Error<T>>::TooManyMethodsHaveSponsoredLimit)407			})?;408			Ok(())409		}410411		/// Is user added to allowlist, or he is owner of specified contract412		pub fn allowed(contract: H160, user: H160) -> bool {413			<Allowlist<T>>::get(contract, user) || <Owner<T>>::get(contract) == user414		}415416		/// Toggle contract allowlist access417		pub fn toggle_allowlist(contract: H160, enabled: bool) {418			<AllowlistEnabled<T>>::insert(contract, enabled)419		}420421		/// Toggle user presence in contract's allowlist422		pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {423			<Allowlist<T>>::insert(contract, user, allowed);424		}425426		/// Throw error if user is not allowed to reconfigure target contract427		pub fn ensure_owner(contract: H160, user: H160) -> DispatchResult {428			ensure!(<Owner<T>>::get(contract) == user, Error::<T>::NoPermission);429			Ok(())430		}431	}432}433434/// Available contract sponsoring modes435#[derive(436	Encode, Decode, Debug, PartialEq, TypeInfo, MaxEncodedLen, Default, AbiCoder, Clone, Copy,437)]438#[repr(u8)]439pub enum SponsoringModeT {440	/// Sponsoring is disabled441	#[default]442	Disabled,443	/// Only users from allowlist will be sponsored444	Allowlisted,445	/// All users will be sponsored446	Generous,447}