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

difftreelog

feat calculate signature in compile time

Trubnikov Sergey2022-10-11parent: #4d4a023.patch.diff
in: master

11 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1096,6 +1096,26 @@
 checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
 
 [[package]]
+name = "const_format"
+version = "0.2.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495"
+dependencies = [
+ "const_format_proc_macros",
+]
+
+[[package]]
+name = "const_format_proc_macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
 name = "constant_time_eq"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2353,6 +2373,7 @@
 version = "0.1.3"
 dependencies = [
  "concat-idents",
+ "const_format",
  "ethereum",
  "evm-coder-procedural",
  "evm-core 0.35.0 (git+https://github.com/uniquenetwork/evm?branch=unique-polkadot-v0.9.30)",
@@ -2362,6 +2383,7 @@
  "impl-trait-for-tuples",
  "pallet-evm 6.0.0-dev (git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit)",
  "primitive-types",
+ "sha3-const",
  "similar-asserts",
  "sp-std 4.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.30)",
 ]
@@ -11085,6 +11107,12 @@
 ]
 
 [[package]]
+name = "sha3-const"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9625d558d174dbdc711248b479271c0fdca39e9d3d67f6e890b3d4251fbf8e"
+
+[[package]]
 name = "sharded-slab"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedcrates/evm-coder/Cargo.tomldiffbeforeafterboth
--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -5,6 +5,10 @@
 edition = "2021"
 
 [dependencies]
+const_format = { version = "0.2.26", default-features = false }
+sha3-const = { version = "0.1.0", default-features = false }
+# Ethereum uses keccak (=sha3) for selectors
+# sha3 = "0.10.1"
 # evm-coder reexports those proc-macro
 evm-coder-procedural = { path = "./procedural" }
 # Evm uses primitive-types for H160, H256 and others
modifiedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -20,6 +20,7 @@
 // about Procedural Macros in Rust book:
 // https://doc.rust-lang.org/reference/procedural-macros.html
 
+use proc_macro2::{TokenStream, Group};
 use quote::{quote, ToTokens};
 use inflector::cases;
 use std::fmt::Write;
@@ -328,6 +329,7 @@
 	}
 }
 
+#[derive(Debug)]
 enum AbiType {
 	// type
 	Plain(Ident),
@@ -473,6 +475,7 @@
 	}
 }
 
+#[derive(Debug)]
 struct MethodArg {
 	name: Ident,
 	camel_name: String,
@@ -722,13 +725,25 @@
 		}
 	}
 
+	fn expand_selector(&self) -> proc_macro2::TokenStream {
+		let custom_signature = self.expand_custom_signature();
+		quote! {
+			 {
+				let a = ::evm_coder::sha3_const::Keccak256::new()
+					.update(#custom_signature.as_bytes())
+					.finalize();
+				[a[0], a[1], a[2], a[3]]
+			}
+		}
+	}
+
 	fn expand_const(&self) -> proc_macro2::TokenStream {
 		let screaming_name = &self.screaming_name;
-		let selector = u32::to_be_bytes(self.selector);
 		let selector_str = &self.selector_str;
+		let selector = &self.expand_selector();
 		quote! {
 			#[doc = #selector_str]
-			const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];
+			const #screaming_name: ::evm_coder::types::bytes4 = #selector;
 		}
 	}
 
@@ -831,6 +846,59 @@
 		}
 	}
 
+	fn expand_custom_signature(&self) -> proc_macro2::TokenStream {
+		let mut first_comma = true;
+		let mut custom_signature = TokenStream::new();
+		let mut template = self.camel_name.clone() + "(";
+		self.args
+			.iter()
+			.filter_map(|a| {
+				if a.is_special() {
+					return None;
+				};
+
+				match a.ty {
+					AbiType::Plain(ref ident) => Some(ident),
+					_ => None,
+				}
+			})
+			.for_each(|ident| {
+				if !first_comma {
+					custom_signature.extend(quote!(,));
+					template.push(',');
+				} else {
+					first_comma = false;
+				};
+				template.push_str("{}");
+				let ident_str = ident.to_string();
+				match ident_str.as_str() {
+					"address" | "uint8" | "uint16" | "uint32" | "uint64" | "uint128"
+					| "uint256" | "bytes4" | "topic" | "string" | "bytes" | "void" | "caller"
+					| "bool" | "" => {
+						custom_signature.extend(quote!(#ident_str));
+					}
+					_ => {
+						custom_signature.extend(quote! {
+							#ident::SIGNATURE_STRING
+						});
+					}
+				}
+			});
+
+		template.push(')');
+		let mut template = quote!(#template);
+		template.extend(quote!(,));
+		template.extend(custom_signature);
+		let custom_signature_group = Group::new(proc_macro2::Delimiter::Parenthesis, template);
+		let mut custom_signature = quote! {
+			::evm_coder::const_format::formatcp!
+		};
+		custom_signature.extend(custom_signature_group.to_token_stream());
+
+		// println!("!!!!! {}", custom_signature);
+		custom_signature
+	}
+
 	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
 		let camel_name = &self.camel_name;
 		let mutability = match self.mutability {
@@ -847,15 +915,17 @@
 			.map(MethodArg::expand_solidity_argument);
 		let docs = &self.docs;
 		let selector_str = &self.selector_str;
-		let selector = self.selector;
+		let selector = &self.expand_selector();
 		let hide = self.hide;
+		let custom_signature = self.expand_custom_signature();
 		let is_payable = self.has_value_args;
 		quote! {
 			SolidityFunction {
 				docs: &[#(#docs),*],
 				selector_str: #selector_str,
-				selector: #selector,
 				hide: #hide,
+				selector: u32::from_be_bytes(#selector),
+				custom_signature: #custom_signature,
 				name: #camel_name,
 				mutability: #mutability,
 				is_payable: #is_payable,
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -90,6 +90,8 @@
 pub use evm_coder_procedural::solidity;
 /// See [`solidity_interface`]
 pub use evm_coder_procedural::weight;
+pub use const_format;
+pub use sha3_const;
 
 /// Derives [`ToLog`] for enum
 ///
@@ -227,6 +229,14 @@
 		}
 	}
 
+	impl SignatureString for EthCrossAccount {
+		const SIGNATURE_STRING: &'static str = "(address,uint256)";
+	}
+
+	pub trait SignatureString {
+		const SIGNATURE_STRING: &'static str;
+	}
+
 	/// Convert `CrossAccountId` to `uint256`.
 	pub fn convert_cross_account_to_uint256<T: pallet_evm::account::Config>(
 		from: &T::CrossAccountId,
@@ -348,6 +358,18 @@
 		assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);
 	}
 
+	// #[test]
+	// fn function_selector_generation_1() {
+	// 	assert_eq!(
+	// 		fn_selector!(transferFromCrossAccountToCrossAccount(
+	// 			EthCrossAccount,
+	// 			EthCrossAccount,
+	// 			uint256
+	// 		)),
+	// 		2543295963
+	// 	);
+	// }
+
 	#[test]
 	fn event_topic_generation() {
 		assert_eq!(
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -35,6 +35,12 @@
 use crate::types::*;
 
 #[derive(Default)]
+pub struct FunctionSelectorMaker {
+	pub name: string,
+	pub args: Vec<fn() -> string>,
+}
+
+#[derive(Default)]
 pub struct TypeCollector {
 	/// Code => id
 	/// id ordering is required to perform topo-sort on the resulting data
@@ -482,11 +488,25 @@
 	View,
 	Mutable,
 }
+
+// fn fn_selector_str(input: &str) -> u32 {
+// 	use sha3::Digest;
+// 	let mut hasher = sha3::Keccak256::new();
+// 	hasher.update(input.as_bytes());
+// 	let result = hasher.finalize();
+
+// 	let mut selector_bytes = [0; 4];
+// 	selector_bytes.copy_from_slice(&result[0..4]);
+
+// 	u32::from_be_bytes(selector_bytes)
+// }
+
 pub struct SolidityFunction<A, R> {
 	pub docs: &'static [&'static str],
 	pub selector_str: &'static str,
 	pub selector: u32,
 	pub hide: bool,
+	pub custom_signature: &'static str,
 	pub name: &'static str,
 	pub args: A,
 	pub result: R,
@@ -512,7 +532,7 @@
 		writeln!(
 			writer,
 			"\t{hide_comment}///  or in textual repr: {}",
-			self.selector_str
+			self.custom_signature
 		)?;
 		write!(writer, "\t{hide_comment}function {}(", self.name)?;
 		self.args.solidity_name(writer, tc)?;
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -27,7 +27,7 @@
 evm-coder = { default-features = false, path = '../../crates/evm-coder' }
 pallet-common = { default-features = false, path = '../../pallets/common' }
 pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
-pallet-evm-transaction-payment = {  default-features = false, path = '../../pallets/evm-transaction-payment' }
+pallet-evm-transaction-payment = { default-features = false, path = '../../pallets/evm-transaction-payment' }
 up-data-structs = { default-features = false, path = '../../primitives/data-structs', features = [
     'serde1',
 ] }
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
before · pallets/evm-contract-helpers/src/eth.rs
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//! Implementation of magic contract1819use core::marker::PhantomData;20use evm_coder::{21	abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,22};23use pallet_evm::{24	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,25	account::CrossAccountId,26};27use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};28use pallet_evm_transaction_payment::CallContext;29use sp_core::{H160, U256};30use up_data_structs::SponsorshipState;31use crate::{32	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit,33	SponsoringRateLimit, SponsoringModeT, Sponsoring,34};35use frame_support::traits::Get;36use up_sponsorship::SponsorshipHandler;37use sp_std::vec::Vec;3839/// Pallet events.40#[derive(ToLog)]41pub enum ContractHelpersEvents {42	/// Contract sponsor was set.43	ContractSponsorSet {44		/// Contract address of the affected collection.45		#[indexed]46		contract_address: address,47		/// New sponsor address.48		sponsor: address,49	},5051	/// New sponsor was confirm.52	ContractSponsorshipConfirmed {53		/// Contract address of the affected collection.54		#[indexed]55		contract_address: address,56		/// New sponsor address.57		sponsor: address,58	},5960	/// Collection sponsor was removed.61	ContractSponsorRemoved {62		/// Contract address of the affected collection.63		#[indexed]64		contract_address: address,65	},66}6768/// See [`ContractHelpersCall`]69pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);70impl<T: Config> WithRecorder<T> for ContractHelpers<T> {71	fn recorder(&self) -> &SubstrateRecorder<T> {72		&self.073	}7475	fn into_recorder(self) -> SubstrateRecorder<T> {76		self.077	}78}7980/// @title Magic contract, which allows users to reconfigure other contracts81#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))]82impl<T: Config> ContractHelpers<T>83where84	T::AccountId: AsRef<[u8; 32]>,85{86	/// Get user, which deployed specified contract87	/// @dev May return zero address in case if contract is deployed88	///  using uniquenetwork evm-migration pallet, or using other terms not89	///  intended by pallet-evm90	/// @dev Returns zero address if contract does not exists91	/// @param contractAddress Contract to get owner of92	/// @return address Owner of contract93	fn contract_owner(&self, contract_address: address) -> Result<address> {94		Ok(<Owner<T>>::get(contract_address))95	}9697	/// Set sponsor.98	/// @param contractAddress Contract for which a sponsor is being established.99	/// @param sponsor User address who set as pending sponsor.100	fn set_sponsor(101		&mut self,102		caller: caller,103		contract_address: address,104		sponsor: address,105	) -> Result<void> {106		self.recorder().consume_sload()?;107		self.recorder().consume_sstore()?;108109		Pallet::<T>::set_sponsor(110			&T::CrossAccountId::from_eth(caller),111			contract_address,112			&T::CrossAccountId::from_eth(sponsor),113		)114		.map_err(dispatch_to_evm::<T>)?;115116		Ok(())117	}118119	/// Set contract as self sponsored.120	///121	/// @param contractAddress Contract for which a self sponsoring is being enabled.122	fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {123		self.recorder().consume_sload()?;124		self.recorder().consume_sstore()?;125126		let caller = T::CrossAccountId::from_eth(caller);127128		Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())129			.map_err(dispatch_to_evm::<T>)?;130131		Pallet::<T>::force_set_sponsor(132			contract_address,133			&T::CrossAccountId::from_eth(contract_address),134		)135		.map_err(dispatch_to_evm::<T>)?;136137		Ok(())138	}139140	/// Remove sponsor.141	///142	/// @param contractAddress Contract for which a sponsorship is being removed.143	fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {144		self.recorder().consume_sload()?;145		self.recorder().consume_sstore()?;146147		Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)148			.map_err(dispatch_to_evm::<T>)?;149150		Ok(())151	}152153	/// Confirm sponsorship.154	///155	/// @dev Caller must be same that set via [`setSponsor`].156	///157	/// @param contractAddress Сontract for which need to confirm sponsorship.158	fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {159		self.recorder().consume_sload()?;160		self.recorder().consume_sstore()?;161162		Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)163			.map_err(dispatch_to_evm::<T>)?;164165		Ok(())166	}167168	/// Get current sponsor.169	///170	/// @param contractAddress The contract for which a sponsor is requested.171	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.172	fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> {173		let sponsor =174			Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;175		Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(176			&sponsor,177		))178	}179180	/// Check tat contract has confirmed sponsor.181	///182	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.183	/// @return **true** if contract has confirmed sponsor.184	fn has_sponsor(&self, contract_address: address) -> Result<bool> {185		Ok(Pallet::<T>::get_sponsor(contract_address).is_some())186	}187188	/// Check tat contract has pending sponsor.189	///190	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.191	/// @return **true** if contract has pending sponsor.192	fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {193		Ok(match Sponsoring::<T>::get(contract_address) {194			SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,195			SponsorshipState::Unconfirmed(_) => true,196		})197	}198199	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {200		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)201	}202203	fn set_sponsoring_mode(204		&mut self,205		caller: caller,206		contract_address: address,207		// TODO: implement support for enums in evm-coder208		mode: uint8,209	) -> Result<void> {210		self.recorder().consume_sload()?;211		self.recorder().consume_sstore()?;212213		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;214		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;215		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);216217		Ok(())218	}219220	/// Get current contract sponsoring rate limit221	/// @param contractAddress Contract to get sponsoring rate limit of222	/// @return uint32 Amount of blocks between two sponsored transactions223	fn sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {224		self.recorder().consume_sload()?;225226		Ok(<SponsoringRateLimit<T>>::get(contract_address)227			.try_into()228			.map_err(|_| "rate limit > u32::MAX")?)229	}230231	/// Set contract sponsoring rate limit232	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should233	///  pass between two sponsored transactions234	/// @param contractAddress Contract to change sponsoring rate limit of235	/// @param rateLimit Target rate limit236	/// @dev Only contract owner can change this setting237	fn set_sponsoring_rate_limit(238		&mut self,239		caller: caller,240		contract_address: address,241		rate_limit: uint32,242	) -> Result<void> {243		self.recorder().consume_sload()?;244		self.recorder().consume_sstore()?;245246		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;247		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());248		Ok(())249	}250251	/// Set contract sponsoring fee limit252	/// @dev Sponsoring fee limit - is maximum fee that could be spent by253	///  single transaction254	/// @param contractAddress Contract to change sponsoring fee limit of255	/// @param feeLimit Fee limit256	/// @dev Only contract owner can change this setting257	fn set_sponsoring_fee_limit(258		&mut self,259		caller: caller,260		contract_address: address,261		fee_limit: uint256,262	) -> Result<void> {263		self.recorder().consume_sload()?;264		self.recorder().consume_sstore()?;265266		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;267		<Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit.into())268			.map_err(dispatch_to_evm::<T>)?;269		Ok(())270	}271272	/// Get current contract sponsoring fee limit273	/// @param contractAddress Contract to get sponsoring fee limit of274	/// @return uint256 Maximum amount of fee that could be spent by single275	///  transaction276	fn sponsoring_fee_limit(&self, contract_address: address) -> Result<uint256> {277		self.recorder().consume_sload()?;278279		Ok(get_sponsoring_fee_limit::<T>(contract_address))280	}281282	/// Is specified user present in contract allow list283	/// @dev Contract owner always implicitly included284	/// @param contractAddress Contract to check allowlist of285	/// @param user User to check286	/// @return bool Is specified users exists in contract allowlist287	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {288		self.0.consume_sload()?;289		Ok(<Pallet<T>>::allowed(contract_address, user))290	}291292	/// Toggle user presence in contract allowlist293	/// @param contractAddress Contract to change allowlist of294	/// @param user Which user presence should be toggled295	/// @param isAllowed `true` if user should be allowed to be sponsored296	///  or call this contract, `false` otherwise297	/// @dev Only contract owner can change this setting298	fn toggle_allowed(299		&mut self,300		caller: caller,301		contract_address: address,302		user: address,303		is_allowed: bool,304	) -> Result<void> {305		self.recorder().consume_sload()?;306		self.recorder().consume_sstore()?;307308		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;309		<Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);310311		Ok(())312	}313314	/// Is this contract has allowlist access enabled315	/// @dev Allowlist always can have users, and it is used for two purposes:316	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist317	///  in case of allowlist access enabled, only users from allowlist may call this contract318	/// @param contractAddress Contract to get allowlist access of319	/// @return bool Is specified contract has allowlist access enabled320	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {321		Ok(<AllowlistEnabled<T>>::get(contract_address))322	}323324	/// Toggle contract allowlist access325	/// @param contractAddress Contract to change allowlist access of326	/// @param enabled Should allowlist access to be enabled?327	fn toggle_allowlist(328		&mut self,329		caller: caller,330		contract_address: address,331		enabled: bool,332	) -> Result<void> {333		self.recorder().consume_sload()?;334		self.recorder().consume_sstore()?;335336		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;337		<Pallet<T>>::toggle_allowlist(contract_address, enabled);338		Ok(())339	}340}341342/// Implements [`OnMethodCall`], which delegates call to [`ContractHelpers`]343pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);344impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>345where346	T::AccountId: AsRef<[u8; 32]>,347{348	fn is_reserved(contract: &sp_core::H160) -> bool {349		contract == &T::ContractAddress::get()350	}351352	fn is_used(contract: &sp_core::H160) -> bool {353		contract == &T::ContractAddress::get()354	}355356	fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {357		// TODO: Extract to another OnMethodCall handler358		if <AllowlistEnabled<T>>::get(handle.code_address())359			&& !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)360		{361			return Some(Err(PrecompileFailure::Revert {362				exit_status: ExitRevert::Reverted,363				output: {364					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));365					writer.string("Target contract is allowlisted");366					writer.finish()367				},368			}));369		}370371		if handle.code_address() != T::ContractAddress::get() {372			return None;373		}374375		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));376		pallet_evm_coder_substrate::call(handle, helpers)377	}378379	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {380		(contract == &T::ContractAddress::get())381			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())382	}383}384385/// Hooks into contract creation, storing owner of newly deployed contract386pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);387impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {388	fn on_create(owner: H160, contract: H160) {389		<Owner<T>>::insert(contract, owner);390	}391}392393/// Bridge to pallet-sponsoring394pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);395impl<T: Config> SponsorshipHandler<T::CrossAccountId, CallContext>396	for HelpersContractSponsoring<T>397{398	fn get_sponsor(399		who: &T::CrossAccountId,400		call_context: &CallContext,401	) -> Option<T::CrossAccountId> {402		let contract_address = call_context.contract_address;403		let mode = <Pallet<T>>::sponsoring_mode(contract_address);404		if mode == SponsoringModeT::Disabled {405			return None;406		}407408		let sponsor = match <Pallet<T>>::get_sponsor(contract_address) {409			Some(sponsor) => sponsor,410			None => return None,411		};412413		if mode == SponsoringModeT::Allowlisted414			&& !<Pallet<T>>::allowed(contract_address, *who.as_eth())415		{416			return None;417		}418		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;419420		if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {421			let limit = <SponsoringRateLimit<T>>::get(contract_address);422423			let timeout = last_tx_block + limit;424			if block_number < timeout {425				return None;426			}427		}428429		let sponsored_fee_limit = get_sponsoring_fee_limit::<T>(contract_address);430431		if call_context.max_fee > sponsored_fee_limit {432			return None;433		}434435		<SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);436437		Some(sponsor)438	}439}440441fn get_sponsoring_fee_limit<T: Config>(contract_address: address) -> uint256 {442	<SponsoringFeeLimit<T>>::get(contract_address)443		.get(&0xffffffff)444		.cloned()445		.unwrap_or(U256::MAX)446}447448generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);449generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);
after · pallets/evm-contract-helpers/src/eth.rs
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//! Implementation of magic contract1819extern crate alloc;20use alloc::{format, string::ToString};21use core::marker::PhantomData;22use evm_coder::{23	abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,24};25use pallet_evm::{26	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, PrecompileHandle,27	account::CrossAccountId,28};29use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder, dispatch_to_evm};30use pallet_evm_transaction_payment::CallContext;31use sp_core::{H160, U256};32use up_data_structs::SponsorshipState;33use crate::{34	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringFeeLimit,35	SponsoringRateLimit, SponsoringModeT, Sponsoring,36};37use frame_support::traits::Get;38use up_sponsorship::SponsorshipHandler;39use sp_std::vec::Vec;4041/// Pallet events.42#[derive(ToLog)]43pub enum ContractHelpersEvents {44	/// Contract sponsor was set.45	ContractSponsorSet {46		/// Contract address of the affected collection.47		#[indexed]48		contract_address: address,49		/// New sponsor address.50		sponsor: address,51	},5253	/// New sponsor was confirm.54	ContractSponsorshipConfirmed {55		/// Contract address of the affected collection.56		#[indexed]57		contract_address: address,58		/// New sponsor address.59		sponsor: address,60	},6162	/// Collection sponsor was removed.63	ContractSponsorRemoved {64		/// Contract address of the affected collection.65		#[indexed]66		contract_address: address,67	},68}6970/// See [`ContractHelpersCall`]71pub struct ContractHelpers<T: Config>(SubstrateRecorder<T>);72impl<T: Config> WithRecorder<T> for ContractHelpers<T> {73	fn recorder(&self) -> &SubstrateRecorder<T> {74		&self.075	}7677	fn into_recorder(self) -> SubstrateRecorder<T> {78		self.079	}80}8182/// @title Magic contract, which allows users to reconfigure other contracts83#[solidity_interface(name = ContractHelpers, events(ContractHelpersEvents))]84impl<T: Config> ContractHelpers<T>85where86	T::AccountId: AsRef<[u8; 32]>,87{88	/// Get user, which deployed specified contract89	/// @dev May return zero address in case if contract is deployed90	///  using uniquenetwork evm-migration pallet, or using other terms not91	///  intended by pallet-evm92	/// @dev Returns zero address if contract does not exists93	/// @param contractAddress Contract to get owner of94	/// @return address Owner of contract95	fn contract_owner(&self, contract_address: address) -> Result<address> {96		Ok(<Owner<T>>::get(contract_address))97	}9899	/// Set sponsor.100	/// @param contractAddress Contract for which a sponsor is being established.101	/// @param sponsor User address who set as pending sponsor.102	fn set_sponsor(103		&mut self,104		caller: caller,105		contract_address: address,106		sponsor: address,107	) -> Result<void> {108		self.recorder().consume_sload()?;109		self.recorder().consume_sstore()?;110111		Pallet::<T>::set_sponsor(112			&T::CrossAccountId::from_eth(caller),113			contract_address,114			&T::CrossAccountId::from_eth(sponsor),115		)116		.map_err(dispatch_to_evm::<T>)?;117118		Ok(())119	}120121	/// Set contract as self sponsored.122	///123	/// @param contractAddress Contract for which a self sponsoring is being enabled.124	fn self_sponsored_enable(&mut self, caller: caller, contract_address: address) -> Result<void> {125		self.recorder().consume_sload()?;126		self.recorder().consume_sstore()?;127128		let caller = T::CrossAccountId::from_eth(caller);129130		Pallet::<T>::ensure_owner(contract_address, *caller.as_eth())131			.map_err(dispatch_to_evm::<T>)?;132133		Pallet::<T>::force_set_sponsor(134			contract_address,135			&T::CrossAccountId::from_eth(contract_address),136		)137		.map_err(dispatch_to_evm::<T>)?;138139		Ok(())140	}141142	/// Remove sponsor.143	///144	/// @param contractAddress Contract for which a sponsorship is being removed.145	fn remove_sponsor(&mut self, caller: caller, contract_address: address) -> Result<void> {146		self.recorder().consume_sload()?;147		self.recorder().consume_sstore()?;148149		Pallet::<T>::remove_sponsor(&T::CrossAccountId::from_eth(caller), contract_address)150			.map_err(dispatch_to_evm::<T>)?;151152		Ok(())153	}154155	/// Confirm sponsorship.156	///157	/// @dev Caller must be same that set via [`setSponsor`].158	///159	/// @param contractAddress Сontract for which need to confirm sponsorship.160	fn confirm_sponsorship(&mut self, caller: caller, contract_address: address) -> Result<void> {161		self.recorder().consume_sload()?;162		self.recorder().consume_sstore()?;163164		Pallet::<T>::confirm_sponsorship(&T::CrossAccountId::from_eth(caller), contract_address)165			.map_err(dispatch_to_evm::<T>)?;166167		Ok(())168	}169170	/// Get current sponsor.171	///172	/// @param contractAddress The contract for which a sponsor is requested.173	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.174	fn sponsor(&self, contract_address: address) -> Result<(address, uint256)> {175		let sponsor =176			Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;177		Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(178			&sponsor,179		))180	}181182	/// Check tat contract has confirmed sponsor.183	///184	/// @param contractAddress The contract for which the presence of a confirmed sponsor is checked.185	/// @return **true** if contract has confirmed sponsor.186	fn has_sponsor(&self, contract_address: address) -> Result<bool> {187		Ok(Pallet::<T>::get_sponsor(contract_address).is_some())188	}189190	/// Check tat contract has pending sponsor.191	///192	/// @param contractAddress The contract for which the presence of a pending sponsor is checked.193	/// @return **true** if contract has pending sponsor.194	fn has_pending_sponsor(&self, contract_address: address) -> Result<bool> {195		Ok(match Sponsoring::<T>::get(contract_address) {196			SponsorshipState::Disabled | SponsorshipState::Confirmed(_) => false,197			SponsorshipState::Unconfirmed(_) => true,198		})199	}200201	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {202		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)203	}204205	fn set_sponsoring_mode(206		&mut self,207		caller: caller,208		contract_address: address,209		// TODO: implement support for enums in evm-coder210		mode: uint8,211	) -> Result<void> {212		self.recorder().consume_sload()?;213		self.recorder().consume_sstore()?;214215		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;216		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;217		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);218219		Ok(())220	}221222	/// Get current contract sponsoring rate limit223	/// @param contractAddress Contract to get sponsoring rate limit of224	/// @return uint32 Amount of blocks between two sponsored transactions225	fn sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {226		self.recorder().consume_sload()?;227228		Ok(<SponsoringRateLimit<T>>::get(contract_address)229			.try_into()230			.map_err(|_| "rate limit > u32::MAX")?)231	}232233	/// Set contract sponsoring rate limit234	/// @dev Sponsoring rate limit - is a minimum amount of blocks that should235	///  pass between two sponsored transactions236	/// @param contractAddress Contract to change sponsoring rate limit of237	/// @param rateLimit Target rate limit238	/// @dev Only contract owner can change this setting239	fn set_sponsoring_rate_limit(240		&mut self,241		caller: caller,242		contract_address: address,243		rate_limit: uint32,244	) -> Result<void> {245		self.recorder().consume_sload()?;246		self.recorder().consume_sstore()?;247248		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;249		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());250		Ok(())251	}252253	/// Set contract sponsoring fee limit254	/// @dev Sponsoring fee limit - is maximum fee that could be spent by255	///  single transaction256	/// @param contractAddress Contract to change sponsoring fee limit of257	/// @param feeLimit Fee limit258	/// @dev Only contract owner can change this setting259	fn set_sponsoring_fee_limit(260		&mut self,261		caller: caller,262		contract_address: address,263		fee_limit: uint256,264	) -> Result<void> {265		self.recorder().consume_sload()?;266		self.recorder().consume_sstore()?;267268		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;269		<Pallet<T>>::set_sponsoring_fee_limit(contract_address, fee_limit.into())270			.map_err(dispatch_to_evm::<T>)?;271		Ok(())272	}273274	/// Get current contract sponsoring fee limit275	/// @param contractAddress Contract to get sponsoring fee limit of276	/// @return uint256 Maximum amount of fee that could be spent by single277	///  transaction278	fn sponsoring_fee_limit(&self, contract_address: address) -> Result<uint256> {279		self.recorder().consume_sload()?;280281		Ok(get_sponsoring_fee_limit::<T>(contract_address))282	}283284	/// Is specified user present in contract allow list285	/// @dev Contract owner always implicitly included286	/// @param contractAddress Contract to check allowlist of287	/// @param user User to check288	/// @return bool Is specified users exists in contract allowlist289	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {290		self.0.consume_sload()?;291		Ok(<Pallet<T>>::allowed(contract_address, user))292	}293294	/// Toggle user presence in contract allowlist295	/// @param contractAddress Contract to change allowlist of296	/// @param user Which user presence should be toggled297	/// @param isAllowed `true` if user should be allowed to be sponsored298	///  or call this contract, `false` otherwise299	/// @dev Only contract owner can change this setting300	fn toggle_allowed(301		&mut self,302		caller: caller,303		contract_address: address,304		user: address,305		is_allowed: bool,306	) -> Result<void> {307		self.recorder().consume_sload()?;308		self.recorder().consume_sstore()?;309310		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;311		<Pallet<T>>::toggle_allowed(contract_address, user, is_allowed);312313		Ok(())314	}315316	/// Is this contract has allowlist access enabled317	/// @dev Allowlist always can have users, and it is used for two purposes:318	///  in case of allowlist sponsoring mode, users will be sponsored if they exist in allowlist319	///  in case of allowlist access enabled, only users from allowlist may call this contract320	/// @param contractAddress Contract to get allowlist access of321	/// @return bool Is specified contract has allowlist access enabled322	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {323		Ok(<AllowlistEnabled<T>>::get(contract_address))324	}325326	/// Toggle contract allowlist access327	/// @param contractAddress Contract to change allowlist access of328	/// @param enabled Should allowlist access to be enabled?329	fn toggle_allowlist(330		&mut self,331		caller: caller,332		contract_address: address,333		enabled: bool,334	) -> Result<void> {335		self.recorder().consume_sload()?;336		self.recorder().consume_sstore()?;337338		<Pallet<T>>::ensure_owner(contract_address, caller).map_err(dispatch_to_evm::<T>)?;339		<Pallet<T>>::toggle_allowlist(contract_address, enabled);340		Ok(())341	}342}343344/// Implements [`OnMethodCall`], which delegates call to [`ContractHelpers`]345pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);346impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T>347where348	T::AccountId: AsRef<[u8; 32]>,349{350	fn is_reserved(contract: &sp_core::H160) -> bool {351		contract == &T::ContractAddress::get()352	}353354	fn is_used(contract: &sp_core::H160) -> bool {355		contract == &T::ContractAddress::get()356	}357358	fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {359		// TODO: Extract to another OnMethodCall handler360		if <AllowlistEnabled<T>>::get(handle.code_address())361			&& !<Pallet<T>>::allowed(handle.code_address(), handle.context().caller)362		{363			return Some(Err(PrecompileFailure::Revert {364				exit_status: ExitRevert::Reverted,365				output: {366					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));367					writer.string("Target contract is allowlisted");368					writer.finish()369				},370			}));371		}372373		if handle.code_address() != T::ContractAddress::get() {374			return None;375		}376377		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(handle.remaining_gas()));378		pallet_evm_coder_substrate::call(handle, helpers)379	}380381	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {382		(contract == &T::ContractAddress::get())383			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())384	}385}386387/// Hooks into contract creation, storing owner of newly deployed contract388pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);389impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {390	fn on_create(owner: H160, contract: H160) {391		<Owner<T>>::insert(contract, owner);392	}393}394395/// Bridge to pallet-sponsoring396pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);397impl<T: Config> SponsorshipHandler<T::CrossAccountId, CallContext>398	for HelpersContractSponsoring<T>399{400	fn get_sponsor(401		who: &T::CrossAccountId,402		call_context: &CallContext,403	) -> Option<T::CrossAccountId> {404		let contract_address = call_context.contract_address;405		let mode = <Pallet<T>>::sponsoring_mode(contract_address);406		if mode == SponsoringModeT::Disabled {407			return None;408		}409410		let sponsor = match <Pallet<T>>::get_sponsor(contract_address) {411			Some(sponsor) => sponsor,412			None => return None,413		};414415		if mode == SponsoringModeT::Allowlisted416			&& !<Pallet<T>>::allowed(contract_address, *who.as_eth())417		{418			return None;419		}420		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;421422		if let Some(last_tx_block) = <SponsorBasket<T>>::get(contract_address, who.as_eth()) {423			let limit = <SponsoringRateLimit<T>>::get(contract_address);424425			let timeout = last_tx_block + limit;426			if block_number < timeout {427				return None;428			}429		}430431		let sponsored_fee_limit = get_sponsoring_fee_limit::<T>(contract_address);432433		if call_context.max_fee > sponsored_fee_limit {434			return None;435		}436437		<SponsorBasket<T>>::insert(contract_address, who.as_eth(), block_number);438439		Some(sponsor)440	}441}442443fn get_sponsoring_fee_limit<T: Config>(contract_address: address) -> uint256 {444	<SponsoringFeeLimit<T>>::get(contract_address)445		.get(&0xffffffff)446		.cloned()447		.unwrap_or(U256::MAX)448}449450generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);451generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -16,6 +16,8 @@
 
 //! ERC-20 standart support implementation.
 
+extern crate alloc;
+use alloc::{format, string::ToString};
 use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
 use core::convert::TryInto;
 use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -20,6 +20,7 @@
 //! Method implementations are mostly doing parameter conversion and calling Nonfungible Pallet methods.
 
 extern crate alloc;
+use alloc::{format, string::ToString};
 use core::{
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,
modifiedpallets/refungible/Cargo.tomldiffbeforeafterboth
--- a/pallets/refungible/Cargo.toml
+++ b/pallets/refungible/Cargo.toml
@@ -26,7 +26,9 @@
 struct-versioning = { path = "../../crates/struct-versioning" }
 up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
 ethereum = { version = "0.12.0", default-features = false }
-scale-info = { version = "2.0.1", default-features = false, features = ["derive",] }
+scale-info = { version = "2.0.1", default-features = false, features = [
+    "derive",
+] }
 derivative = { version = "2.2.0", features = ["use_core"] }
 
 [features]
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -21,6 +21,7 @@
 
 extern crate alloc;
 
+use alloc::{format, string::ToString};
 use core::{
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,