git.delta.rocks / unique-network / refs/commits / 9c9035103c33

difftreelog

feat implement #[weight] macro for evm support

Yaroslav Bolyukin2021-11-05parent: #e4d3792.patch.diff
in: master

35 files changed

modifiedcrates/evm-coder-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/lib.rs
+++ b/crates/evm-coder-macros/src/lib.rs
@@ -211,6 +211,10 @@
 pub fn solidity(_args: TokenStream, stream: TokenStream) -> TokenStream {
 	stream
 }
+#[proc_macro_attribute]
+pub fn weight(_args: TokenStream, stream: TokenStream) -> TokenStream {
+	stream
+}
 
 #[proc_macro_derive(ToLog, attributes(indexed))]
 pub fn to_log(value: TokenStream) -> TokenStream {
modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -30,11 +30,11 @@
 		})
 	}
 
-	fn expand_call_def(&self) -> proc_macro2::TokenStream {
+	fn expand_call_def(&self, gen_ref: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
 		let name = &self.name;
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			#name(#pascal_call_name)
+			#name(#pascal_call_name #gen_ref)
 		}
 	}
 
@@ -45,18 +45,32 @@
 		}
 	}
 
-	fn expand_supports_interface(&self) -> proc_macro2::TokenStream {
+	fn expand_supports_interface(
+		&self,
+		generics: &proc_macro2::TokenStream,
+	) -> proc_macro2::TokenStream {
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			#pascal_call_name::supports_interface(interface_id)
+			<#pascal_call_name #generics>::supports_interface(interface_id)
+		}
+	}
+
+	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
+		let name = &self.name;
+		quote! {
+			Self::#name(call) => call.weight()
 		}
 	}
 
-	fn expand_variant_call(&self) -> proc_macro2::TokenStream {
+	fn expand_variant_call(
+		&self,
+		call_name: &proc_macro2::Ident,
+		generics: &proc_macro2::TokenStream,
+	) -> proc_macro2::TokenStream {
 		let name = &self.name;
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			InternalCall::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name>>::call(self, Msg {
+			#call_name::#name(call) => return <Self as ::evm_coder::Callable<#pascal_call_name #generics>>::call(self, Msg {
 				call,
 				caller: c.caller,
 				value: c.value,
@@ -64,20 +78,20 @@
 		}
 	}
 
-	fn expand_parse(&self) -> proc_macro2::TokenStream {
+	fn expand_parse(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
 		let name = &self.name;
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			if let Some(parsed_call) = #pascal_call_name::parse(method_id, reader)? {
+			if let Some(parsed_call) = <#pascal_call_name #generics>::parse(method_id, reader)? {
 				return Ok(Some(Self::#name(parsed_call)))
 			}
 		}
 	}
 
-	fn expand_generator(&self) -> proc_macro2::TokenStream {
+	fn expand_generator(&self, generics: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			#pascal_call_name::generate_solidity_interface(tc, is_impl);
+			<#pascal_call_name #generics>::generate_solidity_interface(tc, is_impl);
 		}
 	}
 
@@ -351,6 +365,25 @@
 	Pure,
 }
 
+pub struct WeightAttr(syn::Expr);
+
+mod keyword {
+	syn::custom_keyword!(weight);
+}
+
+impl syn::parse::Parse for WeightAttr {
+	fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+		input.parse::<syn::Token![#]>()?;
+		let content;
+		syn::bracketed!(content in input);
+		content.parse::<keyword::weight>()?;
+
+		let weight_content;
+		syn::parenthesized!(weight_content in content);
+		Ok(WeightAttr(weight_content.parse::<syn::Expr>()?))
+	}
+}
+
 struct Method {
 	name: Ident,
 	camel_name: String,
@@ -362,6 +395,7 @@
 	has_normal_args: bool,
 	mutability: Mutability,
 	result: Type,
+	weight: Option<Expr>,
 	docs: Vec<String>,
 }
 impl Method {
@@ -370,6 +404,7 @@
 			rename_selector: None,
 		};
 		let mut docs = Vec::new();
+		let mut weight = None;
 		for attr in &value.attrs {
 			let ident = parse_ident_from_path(&attr.path, false)?;
 			if ident == "solidity" {
@@ -384,6 +419,8 @@
 					_ => unreachable!(),
 				};
 				docs.push(value);
+			} else if ident == "weight" {
+				weight = Some(syn::parse2::<WeightAttr>(attr.to_token_stream())?.0);
 			}
 		}
 		let ident = &value.sig.ident;
@@ -466,6 +503,7 @@
 			has_normal_args,
 			mutability,
 			result: result.clone(),
+			weight,
 			docs,
 		})
 	}
@@ -528,7 +566,7 @@
 		}
 	}
 
-	fn expand_variant_call(&self) -> proc_macro2::TokenStream {
+	fn expand_variant_call(&self, call_name: &proc_macro2::Ident) -> proc_macro2::TokenStream {
 		let pascal_name = &self.pascal_name;
 		let name = &self.name;
 
@@ -555,17 +593,50 @@
 		let args = self.args.iter().map(|a| a.expand_call_arg());
 
 		quote! {
-			InternalCall::#pascal_name #matcher => {
+			#call_name::#pascal_name #matcher => {
 				let result = #receiver #name(
 					#(
 						#args,
 					)*
 				)?;
-				(&result).abi_write(&mut writer);
+				(&result).into_result()
 			}
 		}
 	}
 
+	fn expand_variant_weight(&self) -> proc_macro2::TokenStream {
+		let pascal_name = &self.pascal_name;
+		if let Some(weight) = &self.weight {
+			let matcher = if self.has_normal_args {
+				let names = self
+					.args
+					.iter()
+					.filter(|a| !a.is_special())
+					.map(|a| &a.name);
+
+				quote! {{
+					#(
+						#names,
+					)*
+				}}
+			} else {
+				quote! {}
+			};
+			quote! {
+				Self::#pascal_name #matcher => (#weight).into()
+			}
+		} else {
+			let matcher = if self.has_normal_args {
+				quote! {{..}}
+			} else {
+				quote! {}
+			};
+			quote! {
+				Self::#pascal_name #matcher => ().into()
+			}
+		}
+	}
+
 	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
 		let camel_name = &self.camel_name;
 		let mutability = match self.mutability {
@@ -600,6 +671,42 @@
 	}
 }
 
+fn generics_list(gen: &Generics) -> proc_macro2::TokenStream {
+	if gen.params.is_empty() {
+		return quote! {};
+	}
+	let params = gen.params.iter().map(|p| match p {
+		syn::GenericParam::Type(id) => {
+			let v = &id.ident;
+			quote! {#v}
+		}
+		syn::GenericParam::Lifetime(lt) => {
+			let v = &lt.lifetime;
+			quote! {#v}
+		}
+		syn::GenericParam::Const(c) => {
+			let i = &c.ident;
+			quote! {#i}
+		}
+	});
+	quote! { #(#params),* }
+}
+fn generics_reference(gen: &Generics) -> proc_macro2::TokenStream {
+	if gen.params.is_empty() {
+		return quote! {};
+	}
+	let list = generics_list(gen);
+	quote! { <#list> }
+}
+fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {
+	let list = generics_list(gen);
+	if gen.params.len() == 1 {
+		quote! {#list}
+	} else {
+		quote! { (#list) }
+	}
+}
+
 pub struct SolidityInterface {
 	generics: Generics,
 	name: Box<syn::Type>,
@@ -628,6 +735,8 @@
 		let solidity_name = self.info.name.to_string();
 		let call_name = pascal_ident_to_call(&self.info.name);
 		let generics = self.generics;
+		let gen_ref = generics_reference(&generics);
+		let gen_data = generics_data(&generics);
 
 		let call_sub = self
 			.info
@@ -635,30 +744,46 @@
 			.0
 			.iter()
 			.chain(self.info.is.0.iter())
-			.map(Is::expand_call_def);
+			.map(|c| Is::expand_call_def(c, &gen_ref));
 		let call_parse = self
 			.info
 			.inline_is
 			.0
 			.iter()
 			.chain(self.info.is.0.iter())
-			.map(Is::expand_parse);
+			.map(|is| Is::expand_parse(is, &gen_ref));
 		let call_variants = self
 			.info
 			.inline_is
 			.0
 			.iter()
 			.chain(self.info.is.0.iter())
-			.map(Is::expand_variant_call);
+			.map(|c| Is::expand_variant_call(c, &call_name, &gen_ref));
+		let weight_variants = self
+			.info
+			.inline_is
+			.0
+			.iter()
+			.chain(self.info.is.0.iter())
+			.map(Is::expand_variant_weight);
 
 		let inline_interface_id = self.info.inline_is.0.iter().map(Is::expand_interface_id);
-		let supports_interface = self.info.is.0.iter().map(Is::expand_supports_interface);
+		let supports_interface = self
+			.info
+			.is
+			.0
+			.iter()
+			.map(|is| Is::expand_supports_interface(is, &gen_ref));
 
 		let calls = self.methods.iter().map(Method::expand_call_def);
 		let consts = self.methods.iter().map(Method::expand_const);
 		let interface_id = self.methods.iter().map(Method::expand_interface_id);
 		let parsers = self.methods.iter().map(Method::expand_parse);
-		let call_variants_this = self.methods.iter().map(Method::expand_variant_call);
+		let call_variants_this = self
+			.methods
+			.iter()
+			.map(|m| Method::expand_variant_call(m, &call_name));
+		let weight_variants_this = self.methods.iter().map(Method::expand_variant_weight);
 		let solidity_functions = self.methods.iter().map(Method::expand_solidity_function);
 
 		// TODO: Inline inline_is
@@ -676,15 +801,15 @@
 			.0
 			.iter()
 			.chain(self.info.inline_is.0.iter())
-			.map(Is::expand_generator);
+			.map(|is| Is::expand_generator(is, &gen_ref));
 		let solidity_event_generators = self.info.events.0.iter().map(Is::expand_event_generator);
 
 		// let methods = self.methods.iter().map(Method::solidity_def);
 
 		quote! {
 			#[derive(Debug)]
-			pub enum #call_name {
-				ERC165Call(::evm_coder::ERC165Call),
+			pub enum #call_name #gen_ref {
+				ERC165Call(::evm_coder::ERC165Call, ::core::marker::PhantomData<#gen_data>),
 				#(
 					#calls,
 				)*
@@ -692,11 +817,11 @@
 					#call_sub,
 				)*
 			}
-			impl #call_name {
+			impl #gen_ref #call_name #gen_ref {
 				#(
 					#consts
 				)*
-				pub const fn interface_id() -> u32 {
+				pub fn interface_id() -> u32 {
 					let mut interface_id = 0;
 					#(#interface_id)*
 					#(#inline_interface_id)*
@@ -748,11 +873,14 @@
 					tc.collect(out);
 				}
 			}
-			impl ::evm_coder::Call for #call_name {
+			impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
 				fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
 					use ::evm_coder::abi::AbiRead;
 					match method_id {
-						::evm_coder::ERC165Call::INTERFACE_ID => return Ok(::evm_coder::ERC165Call::parse(method_id, reader)?.map(Self::ERC165Call)),
+						::evm_coder::ERC165Call::INTERFACE_ID => return Ok(
+							::evm_coder::ERC165Call::parse(method_id, reader)?
+							.map(|c| Self::ERC165Call(c, ::core::marker::PhantomData))
+						),
 						#(
 							#parsers,
 						)*
@@ -764,19 +892,33 @@
 					return Ok(None);
 				}
 			}
-			impl #generics ::evm_coder::Callable<#call_name> for #name {
+			impl #generics ::evm_coder::Weighted for #call_name #gen_ref {
+				#[allow(unused_variables)]
+				fn weight(&self) -> ::evm_coder::execution::DispatchInfo {
+					match self {
+						#(
+							#weight_variants,
+						)*
+						// TODO: It should be very cheap, but not free
+						Self::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {..}, _) => 100u64.into(),
+						#(
+							#weight_variants_this,
+						)*
+					}
+				}
+			}
+			impl #generics ::evm_coder::Callable<#call_name #gen_ref> for #name {
 				#[allow(unreachable_code)] // In case of no inner calls
-				fn call(&mut self, c: Msg<#call_name>) -> Result<::evm_coder::abi::AbiWriter> {
+				fn call(&mut self, c: Msg<#call_name #gen_ref>) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {
 					use ::evm_coder::abi::AbiWrite;
-					type InternalCall = #call_name;
 					match c.call {
 						#(
 							#call_variants,
 						)*
-						InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}) => {
+						#call_name::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {interface_id}, _) => {
 							let mut writer = ::evm_coder::abi::AbiWriter::default();
-							writer.bool(&InternalCall::supports_interface(interface_id));
-							return Ok(writer);
+							writer.bool(&<#call_name #gen_ref>::supports_interface(interface_id));
+							return Ok(writer.into());
 						}
 						_ => {},
 					}
@@ -787,7 +929,6 @@
 						)*
 						_ => unreachable!()
 					}
-					Ok(writer)
 				}
 			}
 		}
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -11,7 +11,10 @@
 use evm_core::ExitError;
 use primitive_types::{H160, U256};
 
-use crate::{execution::Error, types::string};
+use crate::{
+	execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},
+	types::string,
+};
 use crate::execution::Result;
 
 const ABI_ALIGNMENT: usize = 32;
@@ -244,6 +247,7 @@
 }
 
 impl_abi_readable!(u32, uint32);
+impl_abi_readable!(u64, uint64);
 impl_abi_readable!(u128, uint128);
 impl_abi_readable!(U256, uint256);
 impl_abi_readable!(H160, address);
@@ -306,6 +310,36 @@
 
 pub trait AbiWrite {
 	fn abi_write(&self, writer: &mut AbiWriter);
+	fn into_result(&self) -> ResultWithPostInfo<AbiWriter> {
+		let mut writer = AbiWriter::new();
+		self.abi_write(&mut writer);
+		Ok(writer.into())
+	}
+}
+
+impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {
+	// this particular AbiWrite implementation should be split to another trait,
+	// which only implements [`into_result`]
+	//
+	// But due to lack of specialization feature in stable Rust, we can't have
+	// blanket impl of this trait `for T where T: AbiWrite`, so here we abusing
+	// default trait methods for it
+	fn abi_write(&self, _writer: &mut AbiWriter) {
+		debug_assert!(false, "shouldn't be called, see comment")
+	}
+	fn into_result(&self) -> ResultWithPostInfo<AbiWriter> {
+		match self {
+			Ok(v) => Ok(WithPostDispatchInfo {
+				post_info: v.post_info.clone(),
+				data: {
+					let mut out = AbiWriter::new();
+					v.data.abi_write(&mut out);
+					out
+				},
+			}),
+			Err(e) => Err(e.clone()),
+		}
+	}
 }
 
 macro_rules! impl_abi_writeable {
modifiedcrates/evm-coder/src/execution.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/execution.rs
+++ b/crates/evm-coder/src/execution.rs
@@ -4,7 +4,9 @@
 #[cfg(feature = "std")]
 use std::string::{String, ToString};
 
-#[derive(Debug)]
+use crate::Weight;
+
+#[derive(Debug, Clone)]
 pub enum Error {
 	Revert(String),
 	Fatal(ExitFatal),
@@ -21,3 +23,55 @@
 }
 
 pub type Result<T> = core::result::Result<T, Error>;
+
+pub struct DispatchInfo {
+	pub weight: Weight,
+}
+
+impl From<Weight> for DispatchInfo {
+	fn from(weight: Weight) -> Self {
+		Self { weight }
+	}
+}
+impl From<()> for DispatchInfo {
+	fn from(_: ()) -> Self {
+		Self { weight: 0 }
+	}
+}
+
+#[derive(Default, Clone)]
+pub struct PostDispatchInfo {
+	actual_weight: Option<Weight>,
+}
+
+impl PostDispatchInfo {
+	pub fn calc_unspent(&self, info: &DispatchInfo) -> Weight {
+		info.weight - self.calc_actual_weight(info)
+	}
+
+	pub fn calc_actual_weight(&self, info: &DispatchInfo) -> Weight {
+		if let Some(actual_weight) = self.actual_weight {
+			actual_weight.min(info.weight)
+		} else {
+			info.weight
+		}
+	}
+}
+
+#[derive(Clone)]
+pub struct WithPostDispatchInfo<T> {
+	pub data: T,
+	pub post_info: PostDispatchInfo,
+}
+
+impl<T> From<T> for WithPostDispatchInfo<T> {
+	fn from(data: T) -> Self {
+		Self {
+			data,
+			post_info: Default::default(),
+		}
+	}
+}
+
+pub type ResultWithPostInfo<T> =
+	core::result::Result<WithPostDispatchInfo<T>, WithPostDispatchInfo<Error>>;
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -3,10 +3,11 @@
 extern crate alloc;
 
 use abi::{AbiRead, AbiReader, AbiWriter};
-pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, ToLog};
+pub use evm_coder_macros::{event_topic, fn_selector, solidity_interface, solidity, weight, ToLog};
 pub mod abi;
 pub mod events;
 pub use events::ToLog;
+use execution::DispatchInfo;
 pub mod execution;
 pub mod solidity;
 
@@ -57,8 +58,14 @@
 	fn parse(selector: u32, input: &mut AbiReader) -> execution::Result<Option<Self>>;
 }
 
+pub type Weight = u64;
+
+pub trait Weighted: Call {
+	fn weight(&self) -> DispatchInfo;
+}
+
 pub trait Callable<C: Call> {
-	fn call(&mut self, call: types::Msg<C>) -> execution::Result<AbiWriter>;
+	fn call(&mut self, call: types::Msg<C>) -> execution::ResultWithPostInfo<AbiWriter>;
 }
 
 /// Implementation is implicitly provided for all interfaces
@@ -84,15 +91,20 @@
 	}
 }
 
+/// Generate "tests", which will generate solidity code on execution and print it to stdout
+/// Script at .maintain/scripts/generate_api.sh can split this output from test runtime
+///
+/// This macro receives type usage as second argument, but you can use anything as generics,
+/// because no bounds are implied
 #[macro_export]
 macro_rules! generate_stubgen {
-	($name:ident, $decl:ident, $is_impl:literal) => {
+	($name:ident, $decl:ty, $is_impl:literal) => {
 		#[test]
 		#[ignore]
 		fn $name() {
 			use evm_coder::solidity::TypeCollector;
 			let mut out = TypeCollector::new();
-			$decl::generate_solidity_interface(&mut out, $is_impl);
+			<$decl>::generate_solidity_interface(&mut out, $is_impl);
 			println!("=== SNIP START ===");
 			println!("// SPDX-License-Identifier: OTHER");
 			println!("// This code is automatically generated");
deletedcrates/evm-coder/tests/a.rsdiffbeforeafterboth
--- a/crates/evm-coder/tests/a.rs
+++ /dev/null
@@ -1,89 +0,0 @@
-#![allow(dead_code)] // This test only checks that macros is not panicking
-
-use evm_coder::{solidity_interface, types::*, ToLog, execution::Result};
-use evm_coder_macros::solidity;
-
-struct Impls;
-
-#[solidity_interface(name = "OurInterface")]
-impl Impls {
-	fn fn_a(&self, _input: uint256) -> Result<bool> {
-		todo!()
-	}
-}
-
-#[solidity_interface(name = "OurInterface1")]
-impl Impls {
-	fn fn_b(&self, _input: uint128) -> Result<uint32> {
-		todo!()
-	}
-}
-
-#[solidity_interface(
-	name = "OurInterface2",
-	is(OurInterface),
-	inline_is(OurInterface1),
-	events(ERC721Log)
-)]
-impl Impls {
-	#[solidity(rename_selector = "fnK")]
-	fn fn_c(&self, _input: uint32) -> Result<uint8> {
-		todo!()
-	}
-	fn fn_d(&self, _value: uint32) -> Result<uint32> {
-		todo!()
-	}
-
-	fn caller_sensitive(&self, _caller: caller) -> Result<uint8> {
-		todo!()
-	}
-	fn payable(&mut self, _value: value) -> Result<uint8> {
-		todo!()
-	}
-}
-
-#[derive(ToLog)]
-enum ERC721Log {
-	Transfer {
-		#[indexed]
-		from: address,
-		#[indexed]
-		to: address,
-		value: uint256,
-	},
-	Eee {
-		#[indexed]
-		aaa: address,
-		bbb: uint256,
-	},
-}
-
-struct ERC20;
-
-#[solidity_interface(name = "ERC20")]
-impl ERC20 {
-	fn decimals(&self) -> Result<uint8> {
-		todo!()
-	}
-	fn balance_of(&self, _owner: address) -> Result<uint256> {
-		todo!()
-	}
-	fn transfer(&mut self, _caller: caller, _to: address, _value: uint256) -> Result<bool> {
-		todo!()
-	}
-	fn transfer_from(
-		&mut self,
-		_caller: caller,
-		_from: address,
-		_to: address,
-		_value: uint256,
-	) -> Result<bool> {
-		todo!()
-	}
-	fn approve(&mut self, _caller: caller, _spender: address, _value: uint256) -> Result<bool> {
-		todo!()
-	}
-	fn allowance(&self, _owner: address, _spender: address) -> Result<uint256> {
-		todo!()
-	}
-}
addedcrates/evm-coder/tests/generics.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/tests/generics.rs
@@ -0,0 +1,20 @@
+use std::marker::PhantomData;
+use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*};
+
+struct Generic<T>(PhantomData<T>);
+
+#[solidity_interface(name = "GenericIs")]
+impl<T> Generic<T> {
+	fn test_1(&self) -> Result<uint256> {
+		todo!()
+	}
+}
+
+#[solidity_interface(name = "Generic", is(GenericIs))]
+impl<T: Into<u32>> Generic<T> {
+	fn test_2(&self) -> Result<uint256> {
+		todo!()
+	}
+}
+
+generate_stubgen!(gen_iface, GenericCall<()>, false);
addedcrates/evm-coder/tests/log.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/tests/log.rs
@@ -0,0 +1,17 @@
+use evm_coder::{ToLog, types::*};
+
+#[derive(ToLog)]
+enum ERC721Log {
+	Transfer {
+		#[indexed]
+		from: address,
+		#[indexed]
+		to: address,
+		value: uint256,
+	},
+	Eee {
+		#[indexed]
+		aaa: address,
+		bbb: uint256,
+	},
+}
addedcrates/evm-coder/tests/random.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/tests/random.rs
@@ -0,0 +1,65 @@
+#![allow(dead_code)] // This test only checks that macros is not panicking
+
+use evm_coder::{ToLog, execution::Result, solidity_interface, types::*};
+use evm_coder_macros::{solidity, weight};
+
+struct Impls;
+
+#[solidity_interface(name = "OurInterface")]
+impl Impls {
+	fn fn_a(&self, _input: uint256) -> Result<bool> {
+		todo!()
+	}
+}
+
+#[solidity_interface(name = "OurInterface1")]
+impl Impls {
+	fn fn_b(&self, _input: uint128) -> Result<uint32> {
+		todo!()
+	}
+}
+
+#[derive(ToLog)]
+enum OurEvents {
+	Event1 {
+		field1: uint32,
+	},
+	Event2 {
+		field1: uint32,
+		#[indexed]
+		field2: uint32,
+	},
+}
+
+#[solidity_interface(
+	name = "OurInterface2",
+	is(OurInterface),
+	inline_is(OurInterface1),
+	events(OurEvents)
+)]
+impl Impls {
+	#[solidity(rename_selector = "fnK")]
+	fn fn_c(&self, _input: uint32) -> Result<uint8> {
+		todo!()
+	}
+	fn fn_d(&self, _value: uint32) -> Result<uint32> {
+		todo!()
+	}
+
+	fn caller_sensitive(&self, _caller: caller) -> Result<uint8> {
+		todo!()
+	}
+	fn payable(&mut self, _value: value) -> Result<uint8> {
+		todo!()
+	}
+
+	#[weight(*_weight)]
+	fn with_weight(&self, _weight: uint64) -> Result<void> {
+		todo!()
+	}
+
+	/// Doccoment example
+	fn with_doc(&self) -> Result<void> {
+		todo!()
+	}
+}
addedcrates/evm-coder/tests/solidity_generation.rsdiffbeforeafterboth
--- /dev/null
+++ b/crates/evm-coder/tests/solidity_generation.rs
@@ -0,0 +1,35 @@
+use evm_coder::{execution::Result, generate_stubgen, solidity_interface, types::*};
+
+struct ERC20;
+
+#[solidity_interface(name = "ERC20")]
+impl ERC20 {
+	fn decimals(&self) -> Result<uint8> {
+		todo!()
+	}
+	/// Get balance of specified owner
+	fn balance_of(&self, _owner: address) -> Result<uint256> {
+		todo!()
+	}
+	fn transfer(&mut self, _caller: caller, _to: address, _value: uint256) -> Result<bool> {
+		todo!()
+	}
+	fn transfer_from(
+		&mut self,
+		_caller: caller,
+		_from: address,
+		_to: address,
+		_value: uint256,
+	) -> Result<bool> {
+		todo!()
+	}
+	fn approve(&mut self, _caller: caller, _spender: address, _value: uint256) -> Result<bool> {
+		todo!()
+	}
+	fn allowance(&self, _owner: address, _spender: address) -> Result<uint256> {
+		todo!()
+	}
+}
+
+generate_stubgen!(gen_impl, ERC20Call, true);
+generate_stubgen!(gen_iface, ERC20Call, false);
modifiednode/cli/Cargo.tomldiffbeforeafterboth
--- a/node/cli/Cargo.toml
+++ b/node/cli/Cargo.toml
@@ -268,13 +268,13 @@
 jsonrpc-core = '18.0.0'
 jsonrpc-pubsub = "18.0.0"
 
-fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-consensus = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-rpc = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-db = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-rpc = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+fc-rpc-core = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-consensus = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-mapping-sync = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-rpc = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-db = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-rpc = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 
 nft-rpc = { path = "../rpc" }
 
modifiednode/rpc/Cargo.tomldiffbeforeafterboth
--- a/node/rpc/Cargo.toml
+++ b/node/rpc/Cargo.toml
@@ -41,12 +41,12 @@
 substrate-frame-rpc-system = { git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 tokio = { version = "0.2.25", features = ["macros", "sync"] }
 
-pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-ethereum = { git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-rpc-core = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-db = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fc-mapping-sync = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 
 pallet-nft = { path = "../../pallets/nft" }
 uc-rpc = { path = "../../client/rpc" }
modifiedpallets/common/Cargo.tomldiffbeforeafterboth
--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -19,7 +19,7 @@
 pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
 evm-coder = { default-features = false, path = '../../crates/evm-coder' }
 
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 serde = { version = "1.0.130", default-features = false }
 scale-info = { version = "1.0.0", default-features = false, features = [
     "derive",
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1,6 +1,7 @@
 #![cfg_attr(not(feature = "std"), no_std)]
 
 use core::ops::{Deref, DerefMut};
+use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use sp_std::vec::Vec;
 use account::CrossAccountId;
 use frame_support::{
@@ -8,6 +9,7 @@
 	ensure, fail,
 	traits::{Imbalance, Get, Currency},
 };
+use pallet_evm::GasWeightMapping;
 use nft_data_structs::{
 	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
 	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
@@ -27,17 +29,22 @@
 pub struct CollectionHandle<T: Config> {
 	pub id: CollectionId,
 	collection: Collection<T>,
-	pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
+	pub recorder: SubstrateRecorder<T>,
 }
+impl<T: Config> WithRecorder<T> for CollectionHandle<T> {
+	fn recorder(&self) -> &SubstrateRecorder<T> {
+		&self.recorder
+	}
+	fn into_recorder(self) -> SubstrateRecorder<T> {
+		self.recorder
+	}
+}
 impl<T: Config> CollectionHandle<T> {
 	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
 		<CollectionById<T>>::get(id).map(|collection| Self {
 			id,
 			collection,
-			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(
-				eth::collection_id_to_address(id),
-				gas_limit,
-			),
+			recorder: SubstrateRecorder::new(eth::collection_id_to_address(id), gas_limit),
 		})
 	}
 	pub fn new(id: CollectionId) -> Option<Self> {
@@ -46,33 +53,30 @@
 	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {
 		Ok(Self::new(id).ok_or_else(|| <Error<T>>::CollectionNotFound)?)
 	}
-	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
-		self.recorder.log_sub(log)
+	pub fn log(&self, log: impl evm_coder::ToLog) {
+		self.recorder.log(log)
 	}
-	pub fn log_infallible(&self, log: impl evm_coder::ToLog) {
-		self.recorder.log_infallible(log)
+	pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {
+		self.recorder
+			.consume_gas(T::GasWeightMapping::weight_to_gas(
+				<T as frame_system::Config>::DbWeight::get()
+					.read
+					.saturating_mul(reads),
+			))
 	}
-	#[allow(dead_code)]
-	fn consume_gas(&self, gas: u64) -> DispatchResult {
-		self.recorder.consume_gas_sub(gas)
-	}
-	pub fn consume_sload(&self) -> DispatchResult {
-		self.recorder.consume_sload_sub()
+	pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {
+		self.recorder
+			.consume_gas(T::GasWeightMapping::weight_to_gas(
+				<T as frame_system::Config>::DbWeight::get()
+					.write
+					.saturating_mul(writes),
+			))
 	}
-	pub fn consume_sstores(&self, amount: usize) -> DispatchResult {
-		self.recorder.consume_sstores_sub(amount)
-	}
-	pub fn consume_sstore(&self) -> DispatchResult {
-		self.recorder.consume_sstore_sub()
-	}
-	pub fn consume_log(&self, topics: usize, data: usize) -> DispatchResult {
-		self.recorder.consume_log_sub(topics, data)
-	}
-	pub fn submit_logs(self) -> DispatchResult {
+	pub fn submit_logs(self) {
 		self.recorder.submit_logs()
 	}
 	pub fn save(self) -> DispatchResult {
-		self.recorder.submit_logs()?;
+		self.recorder.submit_logs();
 		<CollectionById<T>>::insert(self.id, self.collection);
 		Ok(())
 	}
@@ -96,24 +100,20 @@
 		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);
 		Ok(())
 	}
-	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> Result<bool, DispatchError> {
-		self.consume_sload()?;
-
-		Ok(*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject)))
+	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {
+		*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))
 	}
 	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {
-		ensure!(self.is_owner_or_admin(subject)?, <Error<T>>::NoPermission);
+		ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);
 		Ok(())
 	}
-	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
-		Ok(self.limits.owner_can_transfer() && self.is_owner_or_admin(user)?)
+	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {
+		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)
 	}
-	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
-		Ok(self.limits.owner_can_transfer() && self.is_owner_or_admin(user)?)
+	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {
+		self.limits.owner_can_transfer() && self.is_owner_or_admin(user)
 	}
 	pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {
-		self.consume_sload()?;
-
 		ensure!(
 			<Allowlist<T>>::get((self.id, user)),
 			<Error<T>>::AddressNotInAllowlist
modifiedpallets/evm-coder-substrate/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-coder-substrate/Cargo.toml
+++ b/pallets/evm-coder-substrate/Cargo.toml
@@ -4,15 +4,18 @@
 edition = "2018"
 
 [dependencies]
-scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
+scale-info = { version = "1.0.0", default-features = false, features = [
+    "derive",
+] }
 sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 ethereum = { version = "0.9", git = "https://github.com/purestake/ethereum", branch = "joshy-scale-info", default-features = false }
 evm-coder = { default-features = false, path = "../../crates/evm-coder" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
+frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 
 [dependencies.codec]
 default-features = false
@@ -31,4 +34,6 @@
     "pallet-evm/std",
     "frame-support/std",
     "frame-system/std",
+    'frame-benchmarking/std',
 ]
+runtime-benchmarks = ['frame-benchmarking']
addedpallets/evm-coder-substrate/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-coder-substrate/src/benchmarking.rs
@@ -0,0 +1,8 @@
+use super::*;
+
+use frame_benchmarking::benchmarks;
+
+benchmarks! {
+	// gas_per_second {
+	// }: (pallet_evm::)
+}
modifiedpallets/evm-coder-substrate/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-coder-substrate/src/lib.rs
+++ b/pallets/evm-coder-substrate/src/lib.rs
@@ -2,6 +2,8 @@
 
 #[cfg(not(feature = "std"))]
 extern crate alloc;
+// #[cfg(feature = "runtime-benchmarks")]
+// pub mod benchmarking;
 
 pub use pallet::*;
 
@@ -17,7 +19,10 @@
 		types::{Msg, value},
 	};
 	use frame_support::{ensure};
-	use pallet_evm::{ExitError, ExitReason, ExitRevert, ExitSucceed, PrecompileOutput};
+	use pallet_evm::{
+		ExitError, ExitReason, ExitRevert, ExitSucceed, PrecompileOutput, GasWeightMapping,
+	};
+	use frame_system::ensure_signed;
 	pub use frame_support::dispatch::DispatchResult;
 	use pallet_ethereum::EthereumTransactionSender;
 	use sp_std::cell::RefCell;
@@ -25,6 +30,7 @@
 	use sp_core::{H160, H256};
 	use ethereum::Log;
 	use frame_support::{pallet_prelude::*, traits::PalletInfo};
+	use frame_system::pallet_prelude::*;
 
 	/// DispatchError is opaque, but we need to somehow extract correct error in case of OutOfGas failure
 	/// So we have this pallet, which defines OutOfGas error, and knews its own id to check if DispatchError
@@ -40,25 +46,24 @@
 	#[pallet::config]
 	pub trait Config: frame_system::Config {
 		type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;
+		type GasWeightMapping: pallet_evm::GasWeightMapping;
 	}
 
 	#[pallet::pallet]
 	pub struct Pallet<T>(_);
 
-	// FIXME: those items are defined as private in evm_gasometer::consts, and we can't directly use it
-	pub const G_LOG: u64 = 375;
-	pub const G_LOGDATA: u64 = 8;
-	pub const G_LOGTOPIC: u64 = 375;
+	#[pallet::call]
+	impl<T: Config> Pallet<T> {
+		#[pallet::weight(0)]
+		pub fn empty_call(origin: OriginFor<T>) -> DispatchResult {
+			let _sender = ensure_signed(origin)?;
+			Ok(())
+		}
+	}
 
 	// From instabul hardfork configuration: https://github.com/rust-blockchain/evm/blob/fd4fd6acc0ca3208d6770fdb3ba407c94cdf97c6/runtime/src/lib.rs#L284
 	pub const G_SLOAD_WORD: u64 = 800;
 	pub const G_SSTORE_WORD: u64 = 20000;
-
-	fn log_price(data: usize, topics: usize) -> u64 {
-		G_LOG
-			.saturating_add((data as u64).saturating_mul(G_LOGDATA))
-			.saturating_add((topics as u64).saturating_mul(G_LOGTOPIC))
-	}
 
 	pub fn generate_transaction() -> ethereum::TransactionV0 {
 		use ethereum::{TransactionV0, TransactionAction, TransactionSignature};
@@ -98,16 +103,10 @@
 		pub fn is_empty(&self) -> bool {
 			self.logs.borrow().is_empty()
 		}
-		pub fn log_sub(&self, log: impl ToLog) -> DispatchResult {
-			self.log_raw_sub(log.to_log(self.contract))
-		}
-		pub fn log_raw_sub(&self, log: Log) -> DispatchResult {
-			self.consume_gas_sub(log_price(log.data.len(), log.topics.len()))?;
-			self.logs.borrow_mut().push(log);
-			Ok(())
-		}
+		// TODO: Replace with real storage in pallet-ethereum,
+		// same way as it is done with frame_system's Events
 		/// Doesn't consumes any gas, should be used after consume_log_sub
-		pub fn log_infallible(&self, log: impl ToLog) {
+		pub fn log(&self, log: impl ToLog) {
 			self.logs.borrow_mut().push(log.to_log(self.contract));
 		}
 		pub fn retrieve_logs(self) -> Vec<Log> {
@@ -125,9 +124,6 @@
 		}
 		pub fn consume_sstore_sub(&self) -> DispatchResult {
 			self.consume_gas_sub(G_SSTORE_WORD)
-		}
-		pub fn consume_log_sub(&self, topics: usize, data: usize) -> DispatchResult {
-			self.consume_gas_sub(log_price(data, topics))
 		}
 		pub fn consume_gas_sub(&self, gas: u64) -> DispatchResult {
 			ensure!(gas != u64::MAX, Error::<T>::OutOfGas);
@@ -154,6 +150,10 @@
 			*gas_limit -= gas;
 			Ok(())
 		}
+		pub fn return_gas(&self, gas: u64) {
+			let mut gas_limit = self.gas_limit.borrow_mut();
+			*gas_limit += gas;
+		}
 
 		pub fn evm_to_precompile_output(
 			self,
@@ -181,12 +181,16 @@
 			})
 		}
 
-		pub fn submit_logs(self) -> DispatchResult {
+		pub fn submit_logs(self) {
 			let logs = self.retrieve_logs();
 			if logs.is_empty() {
-				return Ok(());
+				return;
 			}
-			T::EthereumTransactionSender::submit_logs_transaction(generate_transaction(), logs)
+			T::EthereumTransactionSender::submit_logs_transaction(
+				Default::default(),
+				generate_transaction(),
+				logs,
+			)
 		}
 	}
 
@@ -215,7 +219,31 @@
 		}
 	}
 
-	pub fn call_internal<C: evm_coder::Call, E: evm_coder::Callable<C>>(
+	pub trait WithRecorder<T: Config> {
+		fn recorder(&self) -> &SubstrateRecorder<T>;
+		fn into_recorder(self) -> SubstrateRecorder<T>;
+	}
+
+	/// Helper to simplify implementing bridge between evm-coder definitions and pallet-evm
+	pub fn call<
+		T: Config,
+		C: evm_coder::Call + evm_coder::Weighted,
+		E: evm_coder::Callable<C> + WithRecorder<T>,
+	>(
+		caller: H160,
+		mut e: E,
+		value: value,
+		input: &[u8],
+	) -> Option<PrecompileOutput> {
+		let result = call_internal(caller, &mut e, value, input);
+		e.into_recorder().evm_to_precompile_output(result)
+	}
+
+	fn call_internal<
+		T: Config,
+		C: evm_coder::Call + evm_coder::Weighted,
+		E: evm_coder::Callable<C> + WithRecorder<T>,
+	>(
 		caller: H160,
 		e: &mut E,
 		value: value,
@@ -227,11 +255,28 @@
 			return Ok(None);
 		}
 		let call = call.unwrap();
-		e.call(Msg {
+
+		let dispatch_info = call.weight();
+		e.recorder()
+			.consume_gas(T::GasWeightMapping::weight_to_gas(dispatch_info.weight))?;
+
+		match e.call(Msg {
 			call,
 			caller,
 			value,
-		})
-		.map(Some)
+		}) {
+			Ok(v) => {
+				let unspent = v.post_info.calc_unspent(&dispatch_info);
+				e.recorder()
+					.return_gas(T::GasWeightMapping::weight_to_gas(unspent));
+				Ok(Some(v.data))
+			}
+			Err(v) => {
+				let unspent = v.post_info.calc_unspent(&dispatch_info);
+				e.recorder()
+					.return_gas(T::GasWeightMapping::weight_to_gas(unspent));
+				Err(v.data)
+			}
+		}
 	}
 }
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -4,7 +4,9 @@
 edition = "2018"
 
 [dependencies]
-scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
+scale-info = { version = "1.0.0", default-features = false, features = [
+    "derive",
+] }
 frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
@@ -12,7 +14,7 @@
 sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 evm-coder = { default-features = false, path = '../../crates/evm-coder' }
 pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 up-sponsorship = { default-features = false, path = '../../primitives/sponsorship' }
 log = "0.4.14"
 
addedpallets/evm-contract-helpers/exp.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/evm-contract-helpers/exp.rs
@@ -0,0 +1,1474 @@
+#![feature(prelude_import)]
+#[prelude_import]
+use std::prelude::rust_2018::*;
+#[macro_use]
+extern crate std;
+pub use pallet::*;
+pub use eth::*;
+pub mod eth {
+    use core::marker::PhantomData;
+    use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
+    use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
+    use pallet_evm::{ExitReason, ExitRevert, OnCreate, OnMethodCall, PrecompileOutput};
+    use sp_core::H160;
+    use crate::{
+        AllowlistEnabled, Config, Owner, Pallet, SelfSponsoring, SponsorBasket, SponsoringRateLimit,
+    };
+    use frame_support::traits::Get;
+    use up_sponsorship::SponsorshipHandler;
+    use sp_std::{convert::TryInto, vec::Vec};
+    struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
+    impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
+        fn recorder(&self) -> &SubstrateRecorder<T> {
+            &self.0
+        }
+        fn into_recorder(self) -> SubstrateRecorder<T> {
+            self.0
+        }
+    }
+    impl<T: Config> ContractHelpers<T> {
+        fn contract_owner(&self, contract_address: address) -> Result<address> {
+            Ok(<Owner<T>>::get(contract_address))
+        }
+        fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
+            Ok(<SelfSponsoring<T>>::get(contract_address))
+        }
+        fn toggle_sponsoring(
+            &mut self,
+            caller: caller,
+            contract_address: address,
+            enabled: bool,
+        ) -> Result<void> {
+            <Pallet<T>>::ensure_owner(contract_address, caller)?;
+            <Pallet<T>>::toggle_sponsoring(contract_address, enabled);
+            Ok(())
+        }
+        fn set_sponsoring_rate_limit(
+            &mut self,
+            caller: caller,
+            contract_address: address,
+            rate_limit: uint32,
+        ) -> Result<void> {
+            <Pallet<T>>::ensure_owner(contract_address, caller)?;
+            <Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
+            Ok(())
+        }
+        fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
+            Ok(<SponsoringRateLimit<T>>::get(contract_address)
+                .try_into()
+                .map_err(|_| "rate limit > u32::MAX")?)
+        }
+        fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
+            Ok(<Pallet<T>>::allowed(contract_address, user, true))
+        }
+        fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
+            Ok(<AllowlistEnabled<T>>::get(contract_address))
+        }
+        fn toggle_allowlist(
+            &mut self,
+            caller: caller,
+            contract_address: address,
+            enabled: bool,
+        ) -> Result<void> {
+            <Pallet<T>>::ensure_owner(contract_address, caller)?;
+            <Pallet<T>>::toggle_allowlist(contract_address, enabled);
+            Ok(())
+        }
+        fn toggle_allowed(
+            &mut self,
+            caller: caller,
+            contract_address: address,
+            user: address,
+            allowed: bool,
+        ) -> Result<void> {
+            <Pallet<T>>::ensure_owner(contract_address, caller)?;
+            <Pallet<T>>::toggle_allowed(contract_address, user, allowed);
+            Ok(())
+        }
+    }
+    pub enum ContractHelpersCall<T: Config> {
+        ERC165Call(::evm_coder::ERC165Call, PhantomData<(T)>),
+        ContractOwner {
+            contract_address: address,
+        },
+        SponsoringEnabled {
+            contract_address: address,
+        },
+        ToggleSponsoring {
+            contract_address: address,
+            enabled: bool,
+        },
+        SetSponsoringRateLimit {
+            contract_address: address,
+            rate_limit: uint32,
+        },
+        GetSponsoringRateLimit {
+            contract_address: address,
+        },
+        Allowed {
+            contract_address: address,
+            user: address,
+        },
+        AllowlistEnabled {
+            contract_address: address,
+        },
+        ToggleAllowlist {
+            contract_address: address,
+            enabled: bool,
+        },
+        ToggleAllowed {
+            contract_address: address,
+            user: address,
+            allowed: bool,
+        },
+    }
+    #[automatically_derived]
+    #[allow(unused_qualifications)]
+    impl<T: ::core::fmt::Debug + Config> ::core::fmt::Debug for ContractHelpersCall<T> {
+        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+            match (&*self,) {
+                (&ContractHelpersCall::ERC165Call(ref __self_0, ref __self_1),) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_tuple(f, "ERC165Call");
+                    let _ = ::core::fmt::DebugTuple::field(debug_trait_builder, &&(*__self_0));
+                    let _ = ::core::fmt::DebugTuple::field(debug_trait_builder, &&(*__self_1));
+                    ::core::fmt::DebugTuple::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::ContractOwner {
+                    contract_address: ref __self_0,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "ContractOwner");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::SponsoringEnabled {
+                    contract_address: ref __self_0,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "SponsoringEnabled");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::ToggleSponsoring {
+                    contract_address: ref __self_0,
+                    enabled: ref __self_1,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "ToggleSponsoring");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "enabled",
+                        &&(*__self_1),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::SetSponsoringRateLimit {
+                    contract_address: ref __self_0,
+                    rate_limit: ref __self_1,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "SetSponsoringRateLimit");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "rate_limit",
+                        &&(*__self_1),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::GetSponsoringRateLimit {
+                    contract_address: ref __self_0,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "GetSponsoringRateLimit");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::Allowed {
+                    contract_address: ref __self_0,
+                    user: ref __self_1,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "Allowed");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    let _ =
+                        ::core::fmt::DebugStruct::field(debug_trait_builder, "user", &&(*__self_1));
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::AllowlistEnabled {
+                    contract_address: ref __self_0,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "AllowlistEnabled");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::ToggleAllowlist {
+                    contract_address: ref __self_0,
+                    enabled: ref __self_1,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "ToggleAllowlist");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "enabled",
+                        &&(*__self_1),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+                (&ContractHelpersCall::ToggleAllowed {
+                    contract_address: ref __self_0,
+                    user: ref __self_1,
+                    allowed: ref __self_2,
+                },) => {
+                    let debug_trait_builder =
+                        &mut ::core::fmt::Formatter::debug_struct(f, "ToggleAllowed");
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "contract_address",
+                        &&(*__self_0),
+                    );
+                    let _ =
+                        ::core::fmt::DebugStruct::field(debug_trait_builder, "user", &&(*__self_1));
+                    let _ = ::core::fmt::DebugStruct::field(
+                        debug_trait_builder,
+                        "allowed",
+                        &&(*__self_2),
+                    );
+                    ::core::fmt::DebugStruct::finish(debug_trait_builder)
+                }
+            }
+        }
+    }
+    impl<T: Config> ContractHelpersCall<T> {
+        #[doc = "contractOwner(address)"]
+        const CONTRACT_OWNER: u32 = 1364373836u32;
+        #[doc = "sponsoringEnabled(address)"]
+        const SPONSORING_ENABLED: u32 = 1613225057u32;
+        #[doc = "toggleSponsoring(address,bool)"]
+        const TOGGLE_SPONSORING: u32 = 4239158662u32;
+        #[doc = "setSponsoringRateLimit(address,uint32)"]
+        const SET_SPONSORING_RATE_LIMIT: u32 = 2008467720u32;
+        #[doc = "getSponsoringRateLimit(address)"]
+        const GET_SPONSORING_RATE_LIMIT: u32 = 1628240573u32;
+        #[doc = "allowed(address,address)"]
+        const ALLOWED: u32 = 1550156133u32;
+        #[doc = "allowlistEnabled(address)"]
+        const ALLOWLIST_ENABLED: u32 = 3346198380u32;
+        #[doc = "toggleAllowlist(address,bool)"]
+        const TOGGLE_ALLOWLIST: u32 = 920527093u32;
+        #[doc = "toggleAllowed(address,address,bool)"]
+        const TOGGLE_ALLOWED: u32 = 1191627804u32;
+        pub const fn interface_id() -> u32 {
+            let mut interface_id = 0;
+            interface_id ^= Self::CONTRACT_OWNER;
+            interface_id ^= Self::SPONSORING_ENABLED;
+            interface_id ^= Self::TOGGLE_SPONSORING;
+            interface_id ^= Self::SET_SPONSORING_RATE_LIMIT;
+            interface_id ^= Self::GET_SPONSORING_RATE_LIMIT;
+            interface_id ^= Self::ALLOWED;
+            interface_id ^= Self::ALLOWLIST_ENABLED;
+            interface_id ^= Self::TOGGLE_ALLOWLIST;
+            interface_id ^= Self::TOGGLE_ALLOWED;
+            interface_id
+        }
+        pub fn supports_interface(interface_id: u32) -> bool {
+            interface_id != 0xffffff
+                && (interface_id == ::evm_coder::ERC165Call::INTERFACE_ID
+                    || interface_id == Self::interface_id())
+        }
+        pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
+            use evm_coder::solidity::*;
+            use core::fmt::Write;
+            let interface = SolidityInterface {
+                name: "ContractHelpers",
+                selector: Self::interface_id(),
+                is: &["Dummy", "ERC165"],
+                functions: (
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "contractOwner(address) 5152b14c",
+                        name: "contractOwner",
+                        mutability: SolidityMutability::View,
+                        args: (<NamedArgument<address>>::new("contractAddress"),),
+                        result: <UnnamedArgument<address>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "sponsoringEnabled(address) 6027dc61",
+                        name: "sponsoringEnabled",
+                        mutability: SolidityMutability::View,
+                        args: (<NamedArgument<address>>::new("contractAddress"),),
+                        result: <UnnamedArgument<bool>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "toggleSponsoring(address,bool) fcac6d86",
+                        name: "toggleSponsoring",
+                        mutability: SolidityMutability::Mutable,
+                        args: (
+                            <NamedArgument<address>>::new("contractAddress"),
+                            <NamedArgument<bool>>::new("enabled"),
+                        ),
+                        result: <UnnamedArgument<void>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "setSponsoringRateLimit(address,uint32) 77b6c908",
+                        name: "setSponsoringRateLimit",
+                        mutability: SolidityMutability::Mutable,
+                        args: (
+                            <NamedArgument<address>>::new("contractAddress"),
+                            <NamedArgument<uint32>>::new("rateLimit"),
+                        ),
+                        result: <UnnamedArgument<void>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "getSponsoringRateLimit(address) 610cfabd",
+                        name: "getSponsoringRateLimit",
+                        mutability: SolidityMutability::View,
+                        args: (<NamedArgument<address>>::new("contractAddress"),),
+                        result: <UnnamedArgument<uint32>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "allowed(address,address) 5c658165",
+                        name: "allowed",
+                        mutability: SolidityMutability::View,
+                        args: (
+                            <NamedArgument<address>>::new("contractAddress"),
+                            <NamedArgument<address>>::new("user"),
+                        ),
+                        result: <UnnamedArgument<bool>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "allowlistEnabled(address) c772ef6c",
+                        name: "allowlistEnabled",
+                        mutability: SolidityMutability::View,
+                        args: (<NamedArgument<address>>::new("contractAddress"),),
+                        result: <UnnamedArgument<bool>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "toggleAllowlist(address,bool) 36de20f5",
+                        name: "toggleAllowlist",
+                        mutability: SolidityMutability::Mutable,
+                        args: (
+                            <NamedArgument<address>>::new("contractAddress"),
+                            <NamedArgument<bool>>::new("enabled"),
+                        ),
+                        result: <UnnamedArgument<void>>::default(),
+                    },
+                    SolidityFunction {
+                        docs: &[],
+                        selector: "toggleAllowed(address,address,bool) 4706cc1c",
+                        name: "toggleAllowed",
+                        mutability: SolidityMutability::Mutable,
+                        args: (
+                            <NamedArgument<address>>::new("contractAddress"),
+                            <NamedArgument<address>>::new("user"),
+                            <NamedArgument<bool>>::new("allowed"),
+                        ),
+                        result: <UnnamedArgument<void>>::default(),
+                    },
+                ),
+            };
+            if is_impl {
+                tc . collect ("// Common stubs holder\ncontract Dummy {\n\tuint8 dummy;\n\tstring stub_error = \"this contract is implemented in native\";\n}\ncontract ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool) {\n\t\trequire(false, stub_error);\n\t\tinterfaceID;\n\t\treturn true;\n\t}\n}\n" . into ()) ;
+            } else {
+                tc . collect ("// Common stubs holder\ninterface Dummy {\n}\ninterface ERC165 is Dummy {\n\tfunction supportsInterface(bytes4 interfaceID) external view returns (bool);\n}\n" . into ()) ;
+            }
+            let mut out = string::new();
+            if "ContractHelpers".starts_with("Inline") {
+                out.push_str("// Inline\n");
+            }
+            let _ = interface.format(is_impl, &mut out, tc);
+            tc.collect(out);
+        }
+    }
+    impl<T: Config> ::evm_coder::Call for ContractHelpersCall<T> {
+        fn parse(
+            method_id: u32,
+            reader: &mut ::evm_coder::abi::AbiReader,
+        ) -> ::evm_coder::execution::Result<Option<Self>> {
+            use ::evm_coder::abi::AbiRead;
+            match method_id {
+                ::evm_coder::ERC165Call::INTERFACE_ID => {
+                    return Ok(
+                        ::evm_coder::ERC165Call::parse(method_id, reader)?.map(Self::ERC165Call)
+                    )
+                }
+                Self::CONTRACT_OWNER => {
+                    return Ok(Some(Self::ContractOwner {
+                        contract_address: reader.abi_read()?,
+                    }))
+                }
+                Self::SPONSORING_ENABLED => {
+                    return Ok(Some(Self::SponsoringEnabled {
+                        contract_address: reader.abi_read()?,
+                    }))
+                }
+                Self::TOGGLE_SPONSORING => {
+                    return Ok(Some(Self::ToggleSponsoring {
+                        contract_address: reader.abi_read()?,
+                        enabled: reader.abi_read()?,
+                    }))
+                }
+                Self::SET_SPONSORING_RATE_LIMIT => {
+                    return Ok(Some(Self::SetSponsoringRateLimit {
+                        contract_address: reader.abi_read()?,
+                        rate_limit: reader.abi_read()?,
+                    }))
+                }
+                Self::GET_SPONSORING_RATE_LIMIT => {
+                    return Ok(Some(Self::GetSponsoringRateLimit {
+                        contract_address: reader.abi_read()?,
+                    }))
+                }
+                Self::ALLOWED => {
+                    return Ok(Some(Self::Allowed {
+                        contract_address: reader.abi_read()?,
+                        user: reader.abi_read()?,
+                    }))
+                }
+                Self::ALLOWLIST_ENABLED => {
+                    return Ok(Some(Self::AllowlistEnabled {
+                        contract_address: reader.abi_read()?,
+                    }))
+                }
+                Self::TOGGLE_ALLOWLIST => {
+                    return Ok(Some(Self::ToggleAllowlist {
+                        contract_address: reader.abi_read()?,
+                        enabled: reader.abi_read()?,
+                    }))
+                }
+                Self::TOGGLE_ALLOWED => {
+                    return Ok(Some(Self::ToggleAllowed {
+                        contract_address: reader.abi_read()?,
+                        user: reader.abi_read()?,
+                        allowed: reader.abi_read()?,
+                    }))
+                }
+                _ => {}
+            }
+            return Ok(None);
+        }
+    }
+    impl<T: Config> ::evm_coder::Weighted for ContractHelpersCall<T> {
+        fn weight(&self) -> ::evm_coder::execution::DispatchInfo {
+            type InternalCall = ContractHelpersCall;
+            match self {
+                InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface { .. }) => {
+                    100u64.into()
+                }
+                InternalCall::ContractOwner { .. } => ().into(),
+                InternalCall::SponsoringEnabled { .. } => ().into(),
+                InternalCall::ToggleSponsoring { .. } => ().into(),
+                InternalCall::SetSponsoringRateLimit { .. } => ().into(),
+                InternalCall::GetSponsoringRateLimit { .. } => ().into(),
+                InternalCall::Allowed { .. } => ().into(),
+                InternalCall::AllowlistEnabled { .. } => ().into(),
+                InternalCall::ToggleAllowlist { .. } => ().into(),
+                InternalCall::ToggleAllowed { .. } => ().into(),
+            }
+        }
+    }
+    impl<T: Config> ::evm_coder::Callable<ContractHelpersCall<T>> for ContractHelpers<T> {
+        #[allow(unreachable_code)]
+        fn call(
+            &mut self,
+            c: Msg<ContractHelpersCall>,
+        ) -> ::evm_coder::execution::ResultWithPostInfo<::evm_coder::abi::AbiWriter> {
+            use ::evm_coder::abi::AbiWrite;
+            type InternalCall = ContractHelpersCall;
+            match c.call {
+                InternalCall::ERC165Call(::evm_coder::ERC165Call::SupportsInterface {
+                    interface_id,
+                }) => {
+                    let mut writer = ::evm_coder::abi::AbiWriter::default();
+                    writer.bool(&InternalCall::supports_interface(interface_id));
+                    return Ok(writer.into());
+                }
+                _ => {}
+            }
+            let mut writer = ::evm_coder::abi::AbiWriter::default();
+            match c.call {
+                InternalCall::ContractOwner { contract_address } => {
+                    let result = self.contract_owner(contract_address)?;
+                    (&result).into_result()
+                }
+                InternalCall::SponsoringEnabled { contract_address } => {
+                    let result = self.sponsoring_enabled(contract_address)?;
+                    (&result).into_result()
+                }
+                InternalCall::ToggleSponsoring {
+                    contract_address,
+                    enabled,
+                } => {
+                    let result =
+                        self.toggle_sponsoring(c.caller.clone(), contract_address, enabled)?;
+                    (&result).into_result()
+                }
+                InternalCall::SetSponsoringRateLimit {
+                    contract_address,
+                    rate_limit,
+                } => {
+                    let result = self.set_sponsoring_rate_limit(
+                        c.caller.clone(),
+                        contract_address,
+                        rate_limit,
+                    )?;
+                    (&result).into_result()
+                }
+                InternalCall::GetSponsoringRateLimit { contract_address } => {
+                    let result = self.get_sponsoring_rate_limit(contract_address)?;
+                    (&result).into_result()
+                }
+                InternalCall::Allowed {
+                    contract_address,
+                    user,
+                } => {
+                    let result = self.allowed(contract_address, user)?;
+                    (&result).into_result()
+                }
+                InternalCall::AllowlistEnabled { contract_address } => {
+                    let result = self.allowlist_enabled(contract_address)?;
+                    (&result).into_result()
+                }
+                InternalCall::ToggleAllowlist {
+                    contract_address,
+                    enabled,
+                } => {
+                    let result =
+                        self.toggle_allowlist(c.caller.clone(), contract_address, enabled)?;
+                    (&result).into_result()
+                }
+                InternalCall::ToggleAllowed {
+                    contract_address,
+                    user,
+                    allowed,
+                } => {
+                    let result =
+                        self.toggle_allowed(c.caller.clone(), contract_address, user, allowed)?;
+                    (&result).into_result()
+                }
+                _ => ::core::panicking::panic("internal error: entered unreachable code"),
+            }
+        }
+    }
+    pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);
+    impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {
+        fn is_reserved(contract: &sp_core::H160) -> bool {
+            contract == &T::ContractAddress::get()
+        }
+        fn is_used(contract: &sp_core::H160) -> bool {
+            contract == &T::ContractAddress::get()
+        }
+        fn call(
+            source: &sp_core::H160,
+            target: &sp_core::H160,
+            gas_left: u64,
+            input: &[u8],
+            value: sp_core::U256,
+        ) -> Option<PrecompileOutput> {
+            if !<Pallet<T>>::allowed(*target, *source, true) {
+                return Some(PrecompileOutput {
+                    exit_status: ExitReason::Revert(ExitRevert::Reverted),
+                    cost: 0,
+                    output: {
+                        let mut writer = AbiWriter::new_call(147028384u32);
+                        writer.string("Target contract is allowlisted");
+                        writer.finish()
+                    },
+                    logs: ::alloc::vec::Vec::new(),
+                });
+            }
+            if target != &T::ContractAddress::get() {
+                return None;
+            }
+            let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(*target, gas_left));
+            pallet_evm_coder_substrate::call(*source, helpers, value, input)
+        }
+        fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {
+            (contract == & T :: ContractAddress :: get ()) . then (| | b"`\xe0`@R`&`\x80\x81\x81R\x90a\x04\xf4`\xa09\x80Qa\x00&\x91`\x01\x91` \x90\x91\x01\x90a\x009V[P4\x80\x15a\x003W`\x00\x80\xfd[Pa\x01\rV[\x82\x80Ta\x00E\x90a\x00\xd2V[\x90`\x00R` `\x00 \x90`\x1f\x01` \x90\x04\x81\x01\x92\x82a\x00gW`\x00\x85Ua\x00\xadV[\x82`\x1f\x10a\x00\x80W\x80Q`\xff\x19\x16\x83\x80\x01\x17\x85Ua\x00\xadV[\x82\x80\x01`\x01\x01\x85U\x82\x15a\x00\xadW\x91\x82\x01[\x82\x81\x11\x15a\x00\xadW\x82Q\x82U\x91` \x01\x91\x90`\x01\x01\x90a\x00\x92V[Pa\x00\xb9\x92\x91Pa\x00\xbdV[P\x90V[[\x80\x82\x11\x15a\x00\xb9W`\x00\x81U`\x01\x01a\x00\xbeV[`\x01\x81\x81\x1c\x90\x82\x16\x80a\x00\xe6W`\x7f\x82\x16\x91P[` \x82\x10\x81\x14\x15a\x01\x07WcNH{q`\xe0\x1b`\x00R`\"`\x04R`$`\x00\xfd[P\x91\x90PV[a\x03\xd8\x80a\x01\x1c`\x009`\x00\xf3\xfe`\x80`@R4\x80\x15a\x00\x10W`\x00\x80\xfd[P`\x046\x10a\x00\x9eW`\x005`\xe0\x1c\x80c`\'\xdca\x11a\x00fW\x80c`\'\xdca\x14a\x01\"W\x80ca\x0c\xfa\xbd\x14a\x010W\x80cw\xb6\xc9\x08\x14a\x01SW\x80c\xc7r\xefl\x14a\x01\"W\x80c\xfc\xacm\x86\x14a\x00\xcbW`\x00\x80\xfd[\x80c\x01\xff\xc9\xa7\x14a\x00\xa3W\x80c6\xde \xf5\x14a\x00\xcbW\x80cG\x06\xcc\x1c\x14a\x00\xe0W\x80cQR\xb1L\x14a\x00\xeeW\x80c\\e\x81e\x14a\x01\x14W[`\x00\x80\xfd[a\x00\xb6a\x00\xb16`\x04a\x01\xa2V[a\x01aV[`@Q\x90\x15\x15\x81R` \x01[`@Q\x80\x91\x03\x90\xf3[a\x00\xdea\x00\xd96`\x04a\x01\xffV[a\x01\x87V[\x00[a\x00\xdea\x00\xd96`\x04a\x022V[a\x00\xfca\x00\xb16`\x04a\x02uV[`@Q`\x01`\x01`\xa0\x1b\x03\x90\x91\x16\x81R` \x01a\x00\xc2V[a\x00\xb6a\x00\xb16`\x04a\x02\x90V[a\x00\xb6a\x00\xb16`\x04a\x02uV[a\x01>a\x00\xb16`\x04a\x02uV[`@Qc\xff\xff\xff\xff\x90\x91\x16\x81R` \x01a\x00\xc2V[a\x00\xdea\x00\xd96`\x04a\x02\xbaV[`\x00`\x01`@QbF\x1b\xcd`\xe5\x1b\x81R`\x04\x01a\x01~\x91\x90a\x02\xfaV[`@Q\x80\x91\x03\x90\xfd[`\x01`@QbF\x1b\xcd`\xe5\x1b\x81R`\x04\x01a\x01~\x91\x90a\x02\xfaV[`\x00` \x82\x84\x03\x12\x15a\x01\xb4W`\x00\x80\xfd[\x815`\x01`\x01`\xe0\x1b\x03\x19\x81\x16\x81\x14a\x01\xccW`\x00\x80\xfd[\x93\x92PPPV[\x805`\x01`\x01`\xa0\x1b\x03\x81\x16\x81\x14a\x01\xeaW`\x00\x80\xfd[\x91\x90PV[\x805\x80\x15\x15\x81\x14a\x01\xeaW`\x00\x80\xfd[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\x12W`\x00\x80\xfd[a\x02\x1b\x83a\x01\xd3V[\x91Pa\x02)` \x84\x01a\x01\xefV[\x90P\x92P\x92\x90PV[`\x00\x80`\x00``\x84\x86\x03\x12\x15a\x02GW`\x00\x80\xfd[a\x02P\x84a\x01\xd3V[\x92Pa\x02^` \x85\x01a\x01\xd3V[\x91Pa\x02l`@\x85\x01a\x01\xefV[\x90P\x92P\x92P\x92V[`\x00` \x82\x84\x03\x12\x15a\x02\x87W`\x00\x80\xfd[a\x01\xcc\x82a\x01\xd3V[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\xa3W`\x00\x80\xfd[a\x02\xac\x83a\x01\xd3V[\x91Pa\x02)` \x84\x01a\x01\xd3V[`\x00\x80`@\x83\x85\x03\x12\x15a\x02\xcdW`\x00\x80\xfd[a\x02\xd6\x83a\x01\xd3V[\x91P` \x83\x015c\xff\xff\xff\xff\x81\x16\x81\x14a\x02\xefW`\x00\x80\xfd[\x80\x91PP\x92P\x92\x90PV[`\x00` \x80\x83R`\x00\x84T\x81`\x01\x82\x81\x1c\x91P\x80\x83\x16\x80a\x03\x1cW`\x7f\x83\x16\x92P[\x85\x83\x10\x81\x14\x15a\x03:WcNH{q`\xe0\x1b\x85R`\"`\x04R`$\x85\xfd[\x87\x86\x01\x83\x81R` \x01\x81\x80\x15a\x03WW`\x01\x81\x14a\x03hWa\x03\x93V[`\xff\x19\x86\x16\x82R\x87\x82\x01\x96Pa\x03\x93V[`\x00\x8b\x81R` \x90 `\x00[\x86\x81\x10\x15a\x03\x8dW\x81T\x84\x82\x01R\x90\x85\x01\x90\x89\x01a\x03tV[\x83\x01\x97PP[P\x94\x99\x98PPPPPPPPPV\xfe\xa2dipfsX\"\x12 \xde\xe1\xb0gnP\xa0\xbb\xa7\xaf\xbek+\xe6S6\n\xcd?\x0c+\x81\xebEq\x8c\xe3\xab\xaaC6UdsolcC\x00\x08\t\x003this contract is implemented in native" . to_vec ())
+        }
+    }
+    pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);
+    impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {
+        fn on_create(owner: H160, contract: H160) {
+            <Owner<T>>::insert(contract, owner);
+        }
+    }
+    pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
+    impl<T: Config> SponsorshipHandler<H160, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
+        fn get_sponsor(who: &H160, call: &(H160, Vec<u8>)) -> Option<H160> {
+            if <SelfSponsoring<T>>::get(&call.0) && <Pallet<T>>::allowed(call.0, *who, false) {
+                let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
+                if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who) {
+                    let rate_limit = <SponsoringRateLimit<T>>::get(&call.0);
+                    let limit_time = last_tx_block + rate_limit;
+                    if block_number > limit_time {
+                        <SponsorBasket<T>>::insert(&call.0, who, block_number);
+                        return Some(call.0);
+                    }
+                } else {
+                    <SponsorBasket<T>>::insert(&call.0, who, block_number);
+                    return Some(call.0);
+                }
+            }
+            None
+        }
+    }
+}
+#[doc = r"
+			The module that hosts all the
+			[FRAME](https://docs.substrate.io/v3/runtime/frame)
+			types needed to add this pallet to a
+			runtime.
+			"]
+pub mod pallet {
+    use evm_coder::execution::Result;
+    use frame_support::pallet_prelude::*;
+    use sp_core::H160;
+    #[doc = r"
+			Configuration trait of this pallet.
+
+			Implement this type for a runtime in order to customize this pallet.
+			"]
+    pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config {
+        type ContractAddress: Get<H160>;
+        type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
+    }
+    #[scale_info(skip_type_params(T), capture_docs = "always")]
+    #[doc = r"
+			Custom [dispatch errors](https://docs.substrate.io/v3/runtime/events-and-errors)
+			of this pallet.
+			"]
+    pub enum Error<T> {
+        #[doc(hidden)]
+        #[codec(skip)]
+        __Ignore(
+            frame_support::sp_std::marker::PhantomData<(T)>,
+            frame_support::Never,
+        ),
+        #[doc = " This method is only executable by owner"]
+        NoPermission,
+    }
+    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
+    const _: () = {
+        impl<T> ::scale_info::TypeInfo for Error<T>
+        where
+            frame_support::sp_std::marker::PhantomData<(T)>: ::scale_info::TypeInfo + 'static,
+            T: 'static,
+        {
+            type Identity = Self;
+            fn type_info() -> ::scale_info::Type {
+                :: scale_info :: Type :: builder () . path (:: scale_info :: Path :: new ("Error" , "pallet_evm_contract_helpers::pallet")) . type_params (< [_] > :: into_vec (box [:: scale_info :: TypeParameter :: new ("T" , :: core :: option :: Option :: None)])) . docs_always (& ["\n\t\t\tCustom [dispatch errors](https://docs.substrate.io/v3/runtime/events-and-errors)\n\t\t\tof this pallet.\n\t\t\t"]) . variant (:: scale_info :: build :: Variants :: new () . variant ("NoPermission" , | v | v . index (0usize as :: core :: primitive :: u8) . docs_always (& ["This method is only executable by owner"])))
+            }
+        };
+    };
+    #[doc = r"
+			The [pallet](https://docs.substrate.io/v3/runtime/frame#pallets) implementing
+			the on-chain logic.
+			"]
+    pub struct Pallet<T>(frame_support::sp_std::marker::PhantomData<(T)>);
+    const _: () = {
+        impl<T> core::clone::Clone for Pallet<T> {
+            fn clone(&self) -> Self {
+                Self(core::clone::Clone::clone(&self.0))
+            }
+        }
+    };
+    const _: () = {
+        impl<T> core::cmp::Eq for Pallet<T> {}
+    };
+    const _: () = {
+        impl<T> core::cmp::PartialEq for Pallet<T> {
+            fn eq(&self, other: &Self) -> bool {
+                true && self.0 == other.0
+            }
+        }
+    };
+    const _: () = {
+        impl<T> core::fmt::Debug for Pallet<T> {
+            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+                fmt.debug_tuple("Pallet").field(&self.0).finish()
+            }
+        }
+    };
+    #[allow(type_alias_bounds)]
+    pub(super) type Owner<T: Config> = StorageMap<
+        _GeneratedPrefixForStorageOwner<T>,
+        Twox128,
+        H160,
+        H160,
+        ValueQuery,
+        frame_support::traits::GetDefault,
+        frame_support::traits::GetDefault,
+    >;
+    #[allow(type_alias_bounds)]
+    pub(super) type SelfSponsoring<T: Config> = StorageMap<
+        _GeneratedPrefixForStorageSelfSponsoring<T>,
+        Twox128,
+        H160,
+        bool,
+        ValueQuery,
+        frame_support::traits::GetDefault,
+        frame_support::traits::GetDefault,
+    >;
+    #[allow(type_alias_bounds)]
+    pub(super) type SponsoringRateLimit<T: Config> = StorageMap<
+        _GeneratedPrefixForStorageSponsoringRateLimit<T>,
+        Twox128,
+        H160,
+        T::BlockNumber,
+        ValueQuery,
+        T::DefaultSponsoringRateLimit,
+        frame_support::traits::GetDefault,
+    >;
+    #[allow(type_alias_bounds)]
+    pub(super) type SponsorBasket<T: Config> = StorageDoubleMap<
+        _GeneratedPrefixForStorageSponsorBasket<T>,
+        Twox128,
+        H160,
+        Twox128,
+        H160,
+        T::BlockNumber,
+        OptionQuery,
+        frame_support::traits::GetDefault,
+        frame_support::traits::GetDefault,
+    >;
+    #[allow(type_alias_bounds)]
+    pub(super) type AllowlistEnabled<T: Config> = StorageMap<
+        _GeneratedPrefixForStorageAllowlistEnabled<T>,
+        Twox128,
+        H160,
+        bool,
+        ValueQuery,
+        frame_support::traits::GetDefault,
+        frame_support::traits::GetDefault,
+    >;
+    #[allow(type_alias_bounds)]
+    pub(super) type Allowlist<T: Config> = StorageDoubleMap<
+        _GeneratedPrefixForStorageAllowlist<T>,
+        Twox128,
+        H160,
+        Twox128,
+        H160,
+        bool,
+        ValueQuery,
+        frame_support::traits::GetDefault,
+        frame_support::traits::GetDefault,
+    >;
+    impl<T: Config> Pallet<T> {
+        pub fn toggle_sponsoring(contract: H160, enabled: bool) {
+            <SelfSponsoring<T>>::insert(contract, enabled);
+        }
+        pub fn set_sponsoring_rate_limit(contract: H160, rate_limit: T::BlockNumber) {
+            <SponsoringRateLimit<T>>::insert(contract, rate_limit);
+        }
+        #[doc = " Default is returned if allowlist is disabled"]
+        pub fn allowed(contract: H160, user: H160, default: bool) -> bool {
+            if !<AllowlistEnabled<T>>::get(contract) {
+                return default;
+            }
+            <Allowlist<T>>::get(&contract, &user) || <Owner<T>>::get(&contract) == user
+        }
+        pub fn toggle_allowlist(contract: H160, enabled: bool) {
+            <AllowlistEnabled<T>>::insert(contract, enabled)
+        }
+        pub fn toggle_allowed(contract: H160, user: H160, allowed: bool) {
+            <Allowlist<T>>::insert(contract, user, allowed);
+        }
+        pub fn ensure_owner(contract: H160, user: H160) -> Result<()> {
+            {
+                if !(<Owner<T>>::get(&contract) == user) {
+                    {
+                        return Err("no permission".into());
+                    };
+                }
+            };
+            Ok(())
+        }
+    }
+    impl<T: Config> Pallet<T> {
+        #[doc(hidden)]
+        pub fn pallet_constants_metadata(
+        ) -> frame_support::sp_std::vec::Vec<frame_support::metadata::PalletConstantMetadata>
+        {
+            ::alloc::vec::Vec::new()
+        }
+    }
+    impl<T: Config> Pallet<T> {
+        pub fn error_metadata() -> Option<frame_support::metadata::PalletErrorMetadata> {
+            Some(frame_support::metadata::PalletErrorMetadata {
+                ty: frame_support::scale_info::meta_type::<Error<T>>(),
+            })
+        }
+    }
+    #[doc = r" Type alias to `Pallet`, to be used by `construct_runtime`."]
+    #[doc = r""]
+    #[doc = r" Generated by `pallet` attribute macro."]
+    #[deprecated(note = "use `Pallet` instead")]
+    #[allow(dead_code)]
+    pub type Module<T> = Pallet<T>;
+    impl<T: Config> frame_support::traits::GetStorageVersion for Pallet<T> {
+        fn current_storage_version() -> frame_support::traits::StorageVersion {
+            frame_support::traits::StorageVersion::default()
+        }
+        fn on_chain_storage_version() -> frame_support::traits::StorageVersion {
+            frame_support::traits::StorageVersion::get::<Self>()
+        }
+    }
+    impl<T: Config> frame_support::traits::OnGenesis for Pallet<T> {
+        fn on_genesis() {
+            let storage_version = frame_support::traits::StorageVersion::default();
+            storage_version.put::<Self>();
+        }
+    }
+    impl<T: Config> frame_support::traits::PalletInfoAccess for Pallet<T> {
+        fn index() -> usize {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::index::<
+                Self,
+            >()
+            .expect(
+                "Pallet is part of the runtime because pallet `Config` trait is \
+						implemented by the runtime",
+            )
+        }
+        fn name() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Self,
+            >()
+            .expect(
+                "Pallet is part of the runtime because pallet `Config` trait is \
+						implemented by the runtime",
+            )
+        }
+        fn module_name() -> &'static str {
+            < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: module_name :: < Self > () . expect ("Pallet is part of the runtime because pallet `Config` trait is \
+						implemented by the runtime")
+        }
+        fn crate_version() -> frame_support::traits::CrateVersion {
+            frame_support::traits::CrateVersion {
+                major: 0u16,
+                minor: 1u8,
+                patch: 0u8,
+            }
+        }
+    }
+    impl<T: Config> frame_support::traits::StorageInfoTrait for Pallet<T> {
+        fn storage_info() -> frame_support::sp_std::vec::Vec<frame_support::traits::StorageInfo> {
+            #[allow(unused_mut)]
+            let mut res = ::alloc::vec::Vec::new();
+            {
+                let mut storage_info = < Owner < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            {
+                let mut storage_info = < SelfSponsoring < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            {
+                let mut storage_info = < SponsoringRateLimit < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            {
+                let mut storage_info = < SponsorBasket < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            {
+                let mut storage_info = < AllowlistEnabled < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            {
+                let mut storage_info = < Allowlist < T > as frame_support :: traits :: PartialStorageInfoTrait > :: partial_storage_info () ;
+                res.append(&mut storage_info);
+            }
+            res
+        }
+    }
+    #[doc(hidden)]
+    pub mod __substrate_call_check {
+        #[doc(hidden)]
+        pub use __is_call_part_defined_0 as is_call_part_defined;
+    }
+    #[doc = r"Contains one variant per dispatchable that can be called by an extrinsic."]
+    #[codec(encode_bound())]
+    #[codec(decode_bound())]
+    #[scale_info(skip_type_params(T), capture_docs = "always")]
+    #[allow(non_camel_case_types)]
+    pub enum Call<T: Config> {
+        #[doc(hidden)]
+        #[codec(skip)]
+        __Ignore(
+            frame_support::sp_std::marker::PhantomData<(T,)>,
+            frame_support::Never,
+        ),
+    }
+    const _: () = {
+        impl<T: Config> core::fmt::Debug for Call<T> {
+            fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+                match *self {
+                    Self::__Ignore(ref _0, ref _1) => fmt
+                        .debug_tuple("Call::__Ignore")
+                        .field(&_0)
+                        .field(&_1)
+                        .finish(),
+                }
+            }
+        }
+    };
+    const _: () = {
+        impl<T: Config> core::clone::Clone for Call<T> {
+            fn clone(&self) -> Self {
+                match self {
+                    Self::__Ignore(ref _0, ref _1) => {
+                        Self::__Ignore(core::clone::Clone::clone(_0), core::clone::Clone::clone(_1))
+                    }
+                }
+            }
+        }
+    };
+    const _: () = {
+        impl<T: Config> core::cmp::Eq for Call<T> {}
+    };
+    const _: () = {
+        impl<T: Config> core::cmp::PartialEq for Call<T> {
+            fn eq(&self, other: &Self) -> bool {
+                match (self, other) {
+                    (Self::__Ignore(_0, _1), Self::__Ignore(_0_other, _1_other)) => {
+                        true && _0 == _0_other && _1 == _1_other
+                    }
+                }
+            }
+        }
+    };
+    const _: () = {
+        #[allow(non_camel_case_types)]
+        impl<T: Config> ::codec::Encode for Call<T> {}
+        impl<T: Config> ::codec::EncodeLike for Call<T> {}
+    };
+    const _: () = {
+        #[allow(non_camel_case_types)]
+        impl<T: Config> ::codec::Decode for Call<T> {
+            fn decode<__CodecInputEdqy: ::codec::Input>(
+                __codec_input_edqy: &mut __CodecInputEdqy,
+            ) -> ::core::result::Result<Self, ::codec::Error> {
+                match __codec_input_edqy
+                    .read_byte()
+                    .map_err(|e| e.chain("Could not decode `Call`, failed to read variant byte"))?
+                {
+                    _ => ::core::result::Result::Err(<_ as ::core::convert::Into<_>>::into(
+                        "Could not decode `Call`, variant doesn\'t exist",
+                    )),
+                }
+            }
+        }
+    };
+    #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
+    const _: () = {
+        impl<T: Config> ::scale_info::TypeInfo for Call<T>
+        where
+            frame_support::sp_std::marker::PhantomData<(T,)>: ::scale_info::TypeInfo + 'static,
+            T: Config + 'static,
+        {
+            type Identity = Self;
+            fn type_info() -> ::scale_info::Type {
+                ::scale_info::Type::builder()
+                    .path(::scale_info::Path::new(
+                        "Call",
+                        "pallet_evm_contract_helpers::pallet",
+                    ))
+                    .type_params(<[_]>::into_vec(box [::scale_info::TypeParameter::new(
+                        "T",
+                        ::core::option::Option::None,
+                    )]))
+                    .docs_always(&[
+                        "Contains one variant per dispatchable that can be called by an extrinsic.",
+                    ])
+                    .variant(::scale_info::build::Variants::new())
+            }
+        };
+    };
+    impl<T: Config> Call<T> {}
+    impl<T: Config> frame_support::dispatch::GetDispatchInfo for Call<T> {
+        fn get_dispatch_info(&self) -> frame_support::dispatch::DispatchInfo {
+            match *self {
+                Self::__Ignore(_, _) => {
+                    ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
+                        &["internal error: entered unreachable code: "],
+                        &match (&"__Ignore cannot be used",) {
+                            (arg0,) => [::core::fmt::ArgumentV1::new(
+                                arg0,
+                                ::core::fmt::Display::fmt,
+                            )],
+                        },
+                    ))
+                }
+            }
+        }
+    }
+    impl<T: Config> frame_support::dispatch::GetCallName for Call<T> {
+        fn get_call_name(&self) -> &'static str {
+            match *self {
+                Self::__Ignore(_, _) => {
+                    ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
+                        &["internal error: entered unreachable code: "],
+                        &match (&"__PhantomItem cannot be used.",) {
+                            (arg0,) => [::core::fmt::ArgumentV1::new(
+                                arg0,
+                                ::core::fmt::Display::fmt,
+                            )],
+                        },
+                    ))
+                }
+            }
+        }
+        fn get_call_names() -> &'static [&'static str] {
+            &[]
+        }
+    }
+    impl<T: Config> frame_support::traits::UnfilteredDispatchable for Call<T> {
+        type Origin = frame_system::pallet_prelude::OriginFor<T>;
+        fn dispatch_bypass_filter(
+            self,
+            origin: Self::Origin,
+        ) -> frame_support::dispatch::DispatchResultWithPostInfo {
+            match self {
+                Self::__Ignore(_, _) => {
+                    let _ = origin;
+                    {
+                        {
+                            ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
+                                &["internal error: entered unreachable code: "],
+                                &match (&"__PhantomItem cannot be used.",) {
+                                    (arg0,) => [::core::fmt::ArgumentV1::new(
+                                        arg0,
+                                        ::core::fmt::Display::fmt,
+                                    )],
+                                },
+                            ))
+                        }
+                    };
+                }
+            }
+        }
+    }
+    impl<T: Config> frame_support::dispatch::Callable<T> for Pallet<T> {
+        type Call = Call<T>;
+    }
+    impl<T: Config> Pallet<T> {
+        #[doc(hidden)]
+        pub fn call_functions() -> frame_support::metadata::PalletCallMetadata {
+            frame_support::scale_info::meta_type::<Call<T>>().into()
+        }
+    }
+    impl<T: Config> frame_support::sp_std::fmt::Debug for Error<T> {
+        fn fmt(
+            &self,
+            f: &mut frame_support::sp_std::fmt::Formatter<'_>,
+        ) -> frame_support::sp_std::fmt::Result {
+            f.write_str(self.as_str())
+        }
+    }
+    impl<T: Config> Error<T> {
+        pub fn as_u8(&self) -> u8 {
+            match &self {
+                Self::__Ignore(_, _) => {
+                    ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
+                        &["internal error: entered unreachable code: "],
+                        &match (&"`__Ignore` can never be constructed",) {
+                            (arg0,) => [::core::fmt::ArgumentV1::new(
+                                arg0,
+                                ::core::fmt::Display::fmt,
+                            )],
+                        },
+                    ))
+                }
+                Self::NoPermission => 0usize as u8,
+            }
+        }
+        pub fn as_str(&self) -> &'static str {
+            match &self {
+                Self::__Ignore(_, _) => {
+                    ::core::panicking::panic_fmt(::core::fmt::Arguments::new_v1(
+                        &["internal error: entered unreachable code: "],
+                        &match (&"`__Ignore` can never be constructed",) {
+                            (arg0,) => [::core::fmt::ArgumentV1::new(
+                                arg0,
+                                ::core::fmt::Display::fmt,
+                            )],
+                        },
+                    ))
+                }
+                Self::NoPermission => "NoPermission",
+            }
+        }
+    }
+    impl<T: Config> From<Error<T>> for &'static str {
+        fn from(err: Error<T>) -> &'static str {
+            err.as_str()
+        }
+    }
+    impl<T: Config> From<Error<T>> for frame_support::sp_runtime::DispatchError {
+        fn from(err: Error<T>) -> Self {
+            let index = < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: index :: < Pallet < T > > () . expect ("Every active module has an index in the runtime; qed") as u8 ;
+            frame_support::sp_runtime::DispatchError::Module {
+                index,
+                error: err.as_u8(),
+                message: Some(err.as_str()),
+            }
+        }
+    }
+    #[doc(hidden)]
+    pub mod __substrate_event_check {
+        #[doc(hidden)]
+        pub use __is_event_part_defined_1 as is_event_part_defined;
+    }
+    impl<T: Config> Pallet<T> {
+        #[doc(hidden)]
+        pub fn storage_metadata() -> frame_support::metadata::PalletStorageMetadata {
+            frame_support :: metadata :: PalletStorageMetadata { prefix : < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: name :: < Pallet < T > > () . expect ("Every active pallet has a name in the runtime; qed") , entries : { # [allow (unused_mut)] let mut entries = :: alloc :: vec :: Vec :: new () ; { < Owner < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SelfSponsoring < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SponsoringRateLimit < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < SponsorBasket < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < AllowlistEnabled < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } { < Allowlist < T > as frame_support :: storage :: StorageEntryMetadataBuilder > :: build_metadata (:: alloc :: vec :: Vec :: new () , & mut entries) ; } entries } , }
+        }
+    }
+    pub(super) struct _GeneratedPrefixForStorageOwner<T>(core::marker::PhantomData<(T,)>);
+    impl<T: Config> frame_support::traits::StorageInstance for _GeneratedPrefixForStorageOwner<T> {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "Owner";
+    }
+    pub(super) struct _GeneratedPrefixForStorageSelfSponsoring<T>(core::marker::PhantomData<(T,)>);
+    impl<T: Config> frame_support::traits::StorageInstance
+        for _GeneratedPrefixForStorageSelfSponsoring<T>
+    {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "SelfSponsoring";
+    }
+    pub(super) struct _GeneratedPrefixForStorageSponsoringRateLimit<T>(
+        core::marker::PhantomData<(T,)>,
+    );
+    impl<T: Config> frame_support::traits::StorageInstance
+        for _GeneratedPrefixForStorageSponsoringRateLimit<T>
+    {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "SponsoringRateLimit";
+    }
+    pub(super) struct _GeneratedPrefixForStorageSponsorBasket<T>(core::marker::PhantomData<(T,)>);
+    impl<T: Config> frame_support::traits::StorageInstance
+        for _GeneratedPrefixForStorageSponsorBasket<T>
+    {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "SponsorBasket";
+    }
+    pub(super) struct _GeneratedPrefixForStorageAllowlistEnabled<T>(
+        core::marker::PhantomData<(T,)>,
+    );
+    impl<T: Config> frame_support::traits::StorageInstance
+        for _GeneratedPrefixForStorageAllowlistEnabled<T>
+    {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "AllowlistEnabled";
+    }
+    pub(super) struct _GeneratedPrefixForStorageAllowlist<T>(core::marker::PhantomData<(T,)>);
+    impl<T: Config> frame_support::traits::StorageInstance for _GeneratedPrefixForStorageAllowlist<T> {
+        fn pallet_prefix() -> &'static str {
+            <<T as frame_system::Config>::PalletInfo as frame_support::traits::PalletInfo>::name::<
+                Pallet<T>,
+            >()
+            .expect("Every active pallet has a name in the runtime; qed")
+        }
+        const STORAGE_PREFIX: &'static str = "Allowlist";
+    }
+    #[doc(hidden)]
+    pub mod __substrate_inherent_check {
+        #[doc(hidden)]
+        pub use __is_inherent_part_defined_2 as is_inherent_part_defined;
+    }
+    #[doc = r" Hidden instance generated to be internally used when module is used without"]
+    #[doc = r" instance."]
+    #[doc(hidden)]
+    pub type __InherentHiddenInstance = ();
+    pub(super) trait Store {
+        type Owner;
+        type SelfSponsoring;
+        type SponsoringRateLimit;
+        type SponsorBasket;
+        type AllowlistEnabled;
+        type Allowlist;
+    }
+    impl<T: Config> Store for Pallet<T> {
+        type Owner = Owner<T>;
+        type SelfSponsoring = SelfSponsoring<T>;
+        type SponsoringRateLimit = SponsoringRateLimit<T>;
+        type SponsorBasket = SponsorBasket<T>;
+        type AllowlistEnabled = AllowlistEnabled<T>;
+        type Allowlist = Allowlist<T>;
+    }
+    impl<T: Config> frame_support::traits::Hooks<<T as frame_system::Config>::BlockNumber>
+        for Pallet<T>
+    {
+    }
+    impl<T: Config> frame_support::traits::OnFinalize<<T as frame_system::Config>::BlockNumber>
+        for Pallet<T>
+    {
+        fn on_finalize(n: <T as frame_system::Config>::BlockNumber) {
+            let __within_span__ = {
+                use ::tracing::__macro_support::Callsite as _;
+                static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
+                    use ::tracing::__macro_support::MacroCallsite;
+                    static META: ::tracing::Metadata<'static> = {
+                        ::tracing_core::metadata::Metadata::new(
+                            "on_finalize",
+                            "pallet_evm_contract_helpers::pallet",
+                            ::tracing::Level::TRACE,
+                            Some("pallets/evm-contract-helpers/src/lib.rs"),
+                            Some(7u32),
+                            Some("pallet_evm_contract_helpers::pallet"),
+                            ::tracing_core::field::FieldSet::new(
+                                &[],
+                                ::tracing_core::callsite::Identifier(&CALLSITE),
+                            ),
+                            ::tracing::metadata::Kind::SPAN,
+                        )
+                    };
+                    MacroCallsite::new(&META)
+                };
+                let mut interest = ::tracing::subscriber::Interest::never();
+                if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
+                    && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
+                    && {
+                        interest = CALLSITE.interest();
+                        !interest.is_never()
+                    }
+                    && CALLSITE.is_enabled(interest)
+                {
+                    let meta = CALLSITE.metadata();
+                    ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
+                } else {
+                    let span = CALLSITE.disabled_span();
+                    {};
+                    span
+                }
+            };
+            let __tracing_guard__ = __within_span__.enter();
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_finalize (n)
+        }
+    }
+    impl<T: Config> frame_support::traits::OnIdle<<T as frame_system::Config>::BlockNumber>
+        for Pallet<T>
+    {
+        fn on_idle(
+            n: <T as frame_system::Config>::BlockNumber,
+            remaining_weight: frame_support::weights::Weight,
+        ) -> frame_support::weights::Weight {
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_idle (n , remaining_weight)
+        }
+    }
+    impl<T: Config> frame_support::traits::OnInitialize<<T as frame_system::Config>::BlockNumber>
+        for Pallet<T>
+    {
+        fn on_initialize(
+            n: <T as frame_system::Config>::BlockNumber,
+        ) -> frame_support::weights::Weight {
+            let __within_span__ = {
+                use ::tracing::__macro_support::Callsite as _;
+                static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
+                    use ::tracing::__macro_support::MacroCallsite;
+                    static META: ::tracing::Metadata<'static> = {
+                        ::tracing_core::metadata::Metadata::new(
+                            "on_initialize",
+                            "pallet_evm_contract_helpers::pallet",
+                            ::tracing::Level::TRACE,
+                            Some("pallets/evm-contract-helpers/src/lib.rs"),
+                            Some(7u32),
+                            Some("pallet_evm_contract_helpers::pallet"),
+                            ::tracing_core::field::FieldSet::new(
+                                &[],
+                                ::tracing_core::callsite::Identifier(&CALLSITE),
+                            ),
+                            ::tracing::metadata::Kind::SPAN,
+                        )
+                    };
+                    MacroCallsite::new(&META)
+                };
+                let mut interest = ::tracing::subscriber::Interest::never();
+                if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
+                    && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
+                    && {
+                        interest = CALLSITE.interest();
+                        !interest.is_never()
+                    }
+                    && CALLSITE.is_enabled(interest)
+                {
+                    let meta = CALLSITE.metadata();
+                    ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
+                } else {
+                    let span = CALLSITE.disabled_span();
+                    {};
+                    span
+                }
+            };
+            let __tracing_guard__ = __within_span__.enter();
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_initialize (n)
+        }
+    }
+    impl<T: Config> frame_support::traits::OnRuntimeUpgrade for Pallet<T> {
+        fn on_runtime_upgrade() -> frame_support::weights::Weight {
+            let __within_span__ = {
+                use ::tracing::__macro_support::Callsite as _;
+                static CALLSITE: ::tracing::__macro_support::MacroCallsite = {
+                    use ::tracing::__macro_support::MacroCallsite;
+                    static META: ::tracing::Metadata<'static> = {
+                        ::tracing_core::metadata::Metadata::new(
+                            "on_runtime_update",
+                            "pallet_evm_contract_helpers::pallet",
+                            ::tracing::Level::TRACE,
+                            Some("pallets/evm-contract-helpers/src/lib.rs"),
+                            Some(7u32),
+                            Some("pallet_evm_contract_helpers::pallet"),
+                            ::tracing_core::field::FieldSet::new(
+                                &[],
+                                ::tracing_core::callsite::Identifier(&CALLSITE),
+                            ),
+                            ::tracing::metadata::Kind::SPAN,
+                        )
+                    };
+                    MacroCallsite::new(&META)
+                };
+                let mut interest = ::tracing::subscriber::Interest::never();
+                if ::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
+                    && ::tracing::Level::TRACE <= ::tracing::level_filters::LevelFilter::current()
+                    && {
+                        interest = CALLSITE.interest();
+                        !interest.is_never()
+                    }
+                    && CALLSITE.is_enabled(interest)
+                {
+                    let meta = CALLSITE.metadata();
+                    ::tracing::Span::new(meta, &{ meta.fields().value_set(&[]) })
+                } else {
+                    let span = CALLSITE.disabled_span();
+                    {};
+                    span
+                }
+            };
+            let __tracing_guard__ = __within_span__.enter();
+            let pallet_name = < < T as frame_system :: Config > :: PalletInfo as frame_support :: traits :: PalletInfo > :: name :: < Self > () . unwrap_or ("<unknown pallet name>") ;
+            {
+                let lvl = ::log::Level::Info;
+                if lvl <= ::log::STATIC_MAX_LEVEL && lvl <= ::log::max_level() {
+                    ::log::__private_api_log(
+                        ::core::fmt::Arguments::new_v1(
+                            &["\u{2705} no migration for "],
+                            &match (&pallet_name,) {
+                                (arg0,) => [::core::fmt::ArgumentV1::new(
+                                    arg0,
+                                    ::core::fmt::Display::fmt,
+                                )],
+                            },
+                        ),
+                        lvl,
+                        &(
+                            frame_support::LOG_TARGET,
+                            "pallet_evm_contract_helpers::pallet",
+                            "pallets/evm-contract-helpers/src/lib.rs",
+                            7u32,
+                        ),
+                    );
+                }
+            };
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: on_runtime_upgrade ()
+        }
+    }
+    impl<T: Config> frame_support::traits::OffchainWorker<<T as frame_system::Config>::BlockNumber>
+        for Pallet<T>
+    {
+        fn offchain_worker(n: <T as frame_system::Config>::BlockNumber) {
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: offchain_worker (n)
+        }
+    }
+    impl<T: Config> frame_support::traits::IntegrityTest for Pallet<T> {
+        fn integrity_test() {
+            < Self as frame_support :: traits :: Hooks < < T as frame_system :: Config > :: BlockNumber > > :: integrity_test ()
+        }
+    }
+    #[doc(hidden)]
+    pub mod __substrate_genesis_config_check {
+        #[doc(hidden)]
+        pub use __is_genesis_config_defined_3 as is_genesis_config_defined;
+        #[doc(hidden)]
+        pub use __is_std_enabled_for_genesis_3 as is_std_enabled_for_genesis;
+    }
+    #[doc(hidden)]
+    pub mod __substrate_origin_check {
+        #[doc(hidden)]
+        pub use __is_origin_part_defined_4 as is_origin_part_defined;
+    }
+    #[doc(hidden)]
+    pub mod __substrate_validate_unsigned_check {
+        #[doc(hidden)]
+        pub use __is_validate_unsigned_part_defined_5 as is_validate_unsigned_part_defined;
+    }
+}
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -1,6 +1,6 @@
 use core::marker::PhantomData;
 use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
-use pallet_evm_coder_substrate::SubstrateRecorder;
+use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use pallet_evm::{ExitReason, ExitRevert, OnCreate, OnMethodCall, PrecompileOutput};
 use sp_core::H160;
 use crate::{
@@ -11,16 +11,23 @@
 use sp_std::{convert::TryInto, vec::Vec};
 
 struct ContractHelpers<T: Config>(SubstrateRecorder<T>);
+impl<T: Config> WithRecorder<T> for ContractHelpers<T> {
+	fn recorder(&self) -> &SubstrateRecorder<T> {
+		&self.0
+	}
 
+	fn into_recorder(self) -> SubstrateRecorder<T> {
+		self.0
+	}
+}
+
 #[solidity_interface(name = "ContractHelpers")]
 impl<T: Config> ContractHelpers<T> {
 	fn contract_owner(&self, contract_address: address) -> Result<address> {
-		self.0.consume_sload()?;
 		Ok(<Owner<T>>::get(contract_address))
 	}
 
 	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {
-		self.0.consume_sload()?;
 		Ok(<SelfSponsoring<T>>::get(contract_address))
 	}
 
@@ -30,9 +37,7 @@
 		contract_address: address,
 		enabled: bool,
 	) -> Result<void> {
-		self.0.consume_sload()?;
 		<Pallet<T>>::ensure_owner(contract_address, caller)?;
-		self.0.consume_sstore()?;
 		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);
 		Ok(())
 	}
@@ -43,27 +48,22 @@
 		contract_address: address,
 		rate_limit: uint32,
 	) -> Result<void> {
-		self.0.consume_sload()?;
 		<Pallet<T>>::ensure_owner(contract_address, caller)?;
-		self.0.consume_sstore()?;
 		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());
 		Ok(())
 	}
 
 	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {
-		self.0.consume_sload()?;
 		Ok(<SponsoringRateLimit<T>>::get(contract_address)
 			.try_into()
 			.map_err(|_| "rate limit > u32::MAX")?)
 	}
 
 	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {
-		self.0.consume_sload()?;
 		Ok(<Pallet<T>>::allowed(contract_address, user, true))
 	}
 
 	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {
-		self.0.consume_sload()?;
 		Ok(<AllowlistEnabled<T>>::get(contract_address))
 	}
 
@@ -73,9 +73,7 @@
 		contract_address: address,
 		enabled: bool,
 	) -> Result<void> {
-		self.0.consume_sload()?;
 		<Pallet<T>>::ensure_owner(contract_address, caller)?;
-		self.0.consume_sstore()?;
 		<Pallet<T>>::toggle_allowlist(contract_address, enabled);
 		Ok(())
 	}
@@ -87,9 +85,7 @@
 		user: address,
 		allowed: bool,
 	) -> Result<void> {
-		self.0.consume_sload()?;
 		<Pallet<T>>::ensure_owner(contract_address, caller)?;
-		self.0.consume_sstore()?;
 		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);
 		Ok(())
 	}
@@ -130,9 +126,8 @@
 			return None;
 		}
 
-		let mut helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(*target, gas_left));
-		let result = pallet_evm_coder_substrate::call_internal(*source, &mut helpers, value, input);
-		helpers.0.evm_to_precompile_output(result)
+		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(*target, gas_left));
+		pallet_evm_coder_substrate::call(*source, helpers, value, input)
 	}
 
 	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {
@@ -170,5 +165,5 @@
 	}
 }
 
-generate_stubgen!(contract_helpers_impl, ContractHelpersCall, true);
-generate_stubgen!(contract_helpers_iface, ContractHelpersCall, false);
+generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);
+generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);
modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -21,6 +21,7 @@
 	}
 }
 
+// Selector: 31acb1fe
 contract ContractHelpers is Dummy, ERC165 {
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
modifiedpallets/evm-migration/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-migration/Cargo.toml
+++ b/pallets/evm-migration/Cargo.toml
@@ -4,7 +4,9 @@
 edition = "2018"
 
 [dependencies]
-scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
+scale-info = { version = "1.0.0", default-features = false, features = [
+    "derive",
+] }
 frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 frame-benchmarking = { default-features = false, optional = true, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
@@ -12,8 +14,8 @@
 sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-io = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 
 [dependencies.codec]
 default-features = false
modifiedpallets/evm-transaction-payment/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-transaction-payment/Cargo.toml
+++ b/pallets/evm-transaction-payment/Cargo.toml
@@ -4,16 +4,18 @@
 edition = "2018"
 
 [dependencies]
-scale-info = { version = "1.0.0", default-features = false, features = ["derive"] }
+scale-info = { version = "1.0.0", default-features = false, features = [
+    "derive",
+] }
 frame-support = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 frame-system = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-runtime = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-std = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-io = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
 sp-core = { default-features = false, git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.12' }
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 up-sponsorship = { default-features = false, path = '../../primitives/sponsorship' }
 
 [dependencies.codec]
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -1,15 +1,18 @@
 use core::char::{REPLACEMENT_CHARACTER, decode_utf16};
 use core::convert::TryInto;
-use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};
 use nft_data_structs::CollectionMode;
 use pallet_common::erc::CommonEvmHandler;
 use sp_core::{H160, U256};
 use sp_std::vec::Vec;
 use pallet_common::account::CrossAccountId;
 use pallet_common::erc::PrecompileOutput;
-use pallet_evm_coder_substrate::{call_internal, dispatch_to_evm};
+use pallet_evm_coder_substrate::{call, dispatch_to_evm};
 
-use crate::{Allowance, Balance, Config, FungibleHandle, Pallet, TotalSupply};
+use crate::{
+	Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,
+	weights::WeightInfo,
+};
 
 #[derive(ToLog)]
 pub enum ERC20Events {
@@ -40,6 +43,7 @@
 		Ok(string::from_utf8_lossy(&self.token_prefix).into())
 	}
 	fn total_supply(&self) -> Result<uint256> {
+		self.consume_store_reads(1)?;
 		Ok(<TotalSupply<T>>::get(self.id).into())
 	}
 
@@ -51,10 +55,12 @@
 		})
 	}
 	fn balance_of(&self, owner: address) -> Result<uint256> {
+		self.consume_store_reads(1)?;
 		let owner = T::CrossAccountId::from_eth(owner);
 		let balance = <Balance<T>>::get((self.id, owner));
 		Ok(balance.into())
 	}
+	#[weight(<SelfWeightOf<T>>::transfer())]
 	fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let to = T::CrossAccountId::from_eth(to);
@@ -63,6 +69,7 @@
 		<Pallet<T>>::transfer(self, &caller, &to, amount).map_err(|_| "transfer error")?;
 		Ok(true)
 	}
+	#[weight(<SelfWeightOf<T>>::transfer_from())]
 	fn transfer_from(
 		&mut self,
 		caller: caller,
@@ -79,6 +86,7 @@
 			.map_err(dispatch_to_evm::<T>)?;
 		Ok(true)
 	}
+	#[weight(<SelfWeightOf<T>>::approve())]
 	fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let spender = T::CrossAccountId::from_eth(spender);
@@ -89,6 +97,7 @@
 		Ok(true)
 	}
 	fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+		self.consume_store_reads(1)?;
 		let owner = T::CrossAccountId::from_eth(owner);
 		let spender = T::CrossAccountId::from_eth(spender);
 
@@ -98,6 +107,7 @@
 
 #[solidity_interface(name = "ERC20UniqueExtensions")]
 impl<T: Config> FungibleHandle<T> {
+	#[weight(<SelfWeightOf<T>>::burn_from())]
 	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {
 		let caller = T::CrossAccountId::from_eth(caller);
 		let from = T::CrossAccountId::from_eth(from);
@@ -111,14 +121,13 @@
 #[solidity_interface(name = "UniqueFungible", is(ERC20))]
 impl<T: Config> FungibleHandle<T> {}
 
-generate_stubgen!(gen_impl, UniqueFungibleCall, true);
-generate_stubgen!(gen_iface, UniqueFungibleCall, false);
+generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);
+generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);
 
 impl<T: Config> CommonEvmHandler for FungibleHandle<T> {
 	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");
 
-	fn call(mut self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {
-		let result = call_internal::<UniqueFungibleCall, _>(*source, &mut self, value, input);
-		self.0.recorder.evm_to_precompile_output(result)
+	fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {
+		call::<T, UniqueFungibleCall<T>, _>(*source, self, value, input)
 	}
 }
modifiedpallets/fungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -6,6 +6,7 @@
 use pallet_common::{
 	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
 };
+use pallet_evm_coder_substrate::WithRecorder;
 use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
 use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
@@ -82,6 +83,14 @@
 		self.0
 	}
 }
+impl<T: Config> WithRecorder<T> for FungibleHandle<T> {
+	fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {
+		self.0.recorder()
+	}
+	fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {
+		self.0.into_recorder()
+	}
+}
 impl<T: Config> Deref for FungibleHandle<T> {
 	type Target = pallet_common::CollectionHandle<T>;
 
@@ -136,7 +145,7 @@
 		}
 		<TotalSupply<T>>::insert(collection.id, total_supply);
 
-		collection.log_infallible(ERC20Events::Transfer {
+		collection.log(ERC20Events::Transfer {
 			from: *owner.as_eth(),
 			to: H160::default(),
 			value: amount.into(),
@@ -179,11 +188,6 @@
 		} else {
 			None
 		};
-
-		collection.consume_sstore()?;
-		collection.consume_sstore()?;
-		collection.consume_log(2, 32)?;
-		collection.consume_sstore()?;
 
 		// =========
 
@@ -197,7 +201,7 @@
 			<Balance<T>>::insert((collection.id, to), balance_to);
 		}
 
-		collection.log_infallible(ERC20Events::Transfer {
+		collection.log(ERC20Events::Transfer {
 			from: *from.as_eth(),
 			to: *to.as_eth(),
 			value: amount.into(),
@@ -217,8 +221,7 @@
 		sender: &T::CrossAccountId,
 		data: Vec<CreateItemData<T>>,
 	) -> DispatchResult {
-		let unrestricted_minting = collection.is_owner_or_admin(sender)?;
-		if !unrestricted_minting {
+		if !collection.is_owner_or_admin(sender) {
 			ensure!(
 				collection.mint_mode,
 				<CommonError<T>>::PublicMintingNotAllowed
@@ -241,20 +244,12 @@
 			.ok_or(ArithmeticError::Overflow)?;
 
 		for (user, amount) in data.into_iter() {
-			collection.consume_sload()?;
 			let balance = balances
 				.entry(user.clone())
 				.or_insert_with(|| <Balance<T>>::get((collection.id, user)));
 			*balance = (*balance)
 				.checked_add(amount)
 				.ok_or(ArithmeticError::Overflow)?;
-		}
-
-		collection.consume_sstore()?;
-		for _ in &balances {
-			collection.consume_sstore()?;
-			collection.consume_log(2, 32)?;
-			collection.consume_sstore()?;
 		}
 
 		// =========
@@ -263,7 +258,7 @@
 		for (user, amount) in balances {
 			<Balance<T>>::insert((collection.id, &user), amount);
 
-			collection.log_infallible(ERC20Events::Transfer {
+			collection.log(ERC20Events::Transfer {
 				from: H160::default(),
 				to: *user.as_eth(),
 				value: amount.into(),
@@ -287,7 +282,7 @@
 	) {
 		<Allowance<T>>::insert((collection.id, owner, spender), amount);
 
-		collection.log_infallible(ERC20Events::Approval {
+		collection.log(ERC20Events::Approval {
 			owner: *owner.as_eth(),
 			spender: *spender.as_eth(),
 			value: amount.into(),
@@ -314,7 +309,7 @@
 
 		if <Balance<T>>::get((collection.id, owner)) < amount {
 			ensure!(
-				collection.ignores_owned_amount(owner)?,
+				collection.ignores_owned_amount(owner),
 				<CommonError<T>>::CantApproveMoreThanOwned
 			);
 		}
@@ -343,7 +338,7 @@
 		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);
 		if allowance.is_none() {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
@@ -374,7 +369,7 @@
 		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);
 		if allowance.is_none() {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -146,9 +146,9 @@
     "serde_no_std",
 ] }
 
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 hex-literal = "0.3.3"
 
 pallet-common = { default-features = false, path = "../common" }
modifiedpallets/nft/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -155,12 +155,13 @@
 
 		let cl = CollectionLimits {
 			account_token_ownership_limit: Some(0),
-			sponsored_data_size: 0,
-			token_limit: 1,
-			sponsor_transfer_timeout: 0,
-			owner_can_destroy: true,
-			owner_can_transfer: true,
+			sponsored_data_size: Some(0),
+			token_limit: Some(1),
+			sponsor_transfer_timeout: Some(0),
+			owner_can_destroy: Some(true),
+			owner_can_transfer: Some(true),
 			sponsored_data_rate_limit: None,
+			transfers_enabled: Some(true),
 		};
 	}: set_collection_limits(RawOrigin::Signed(caller.clone()), collection, cl)
 
modifiedpallets/nft/src/dispatch.rsdiffbeforeafterboth
--- a/pallets/nft/src/dispatch.rs
+++ b/pallets/nft/src/dispatch.rs
@@ -83,10 +83,6 @@
 		_ => {}
 	}
 
-	// TODO: Make submit_logs infallible, but it shouldn't fail here anyway
-	dispatched
-		.into_inner()
-		.submit_logs()
-		.expect("should succeed");
+	dispatched.into_inner().submit_logs();
 	result
 }
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -31,7 +31,7 @@
 	let (method_id, mut reader) = AbiReader::new_call(call).map_err(|_| AnyError)?;
 	match &collection.mode {
 		crate::CollectionMode::NFT => {
-			let call: UniqueNFTCall = UniqueNFTCall::parse(method_id, &mut reader)
+			let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader)
 				.map_err(|_| AnyError)?
 				.ok_or(AnyError)?;
 			match call {
@@ -63,7 +63,7 @@
 			}
 		}
 		crate::CollectionMode::Fungible(_) => {
-			let call: UniqueFungibleCall = UniqueFungibleCall::parse(method_id, &mut reader)
+			let call = <UniqueFungibleCall<T>>::parse(method_id, &mut reader)
 				.map_err(|_| AnyError)?
 				.ok_or(AnyError)?;
 			#[allow(clippy::single_match)]
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
2 char::{REPLACEMENT_CHARACTER, decode_utf16},2 char::{REPLACEMENT_CHARACTER, decode_utf16},
3 convert::TryInto,3 convert::TryInto,
4};4};
5use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*};5use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
6use frame_support::BoundedVec;6use frame_support::BoundedVec;
7use nft_data_structs::TokenId;7use nft_data_structs::TokenId;
8use pallet_evm_coder_substrate::dispatch_to_evm;8use pallet_evm_coder_substrate::dispatch_to_evm;
9use sp_core::{H160, U256};9use sp_core::{H160, U256};
10use sp_std::{vec::Vec, vec};10use sp_std::{vec::Vec, vec};
11use pallet_common::{account::CrossAccountId, erc::CommonEvmHandler};11use pallet_common::{account::CrossAccountId, erc::CommonEvmHandler};
12use pallet_evm_coder_substrate::call_internal;12use pallet_evm_coder_substrate::call;
13use pallet_common::erc::PrecompileOutput;13use pallet_common::erc::PrecompileOutput;
1414
15use crate::{15use crate::{
16 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,16 AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
17 SelfWeightOf, weights::WeightInfo,
17};18};
1819
19#[derive(ToLog)]20#[derive(ToLog)]
64 /// Returns token's const_metadata65 /// Returns token's const_metadata
65 #[solidity(rename_selector = "tokenURI")]66 #[solidity(rename_selector = "tokenURI")]
66 fn token_uri(&self, token_id: uint256) -> Result<string> {67 fn token_uri(&self, token_id: uint256) -> Result<string> {
68 self.consume_store_reads(1)?;
67 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;69 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
68 Ok(string::from_utf8_lossy(70 Ok(string::from_utf8_lossy(
69 &<TokenData<T>>::get((self.id, token_id))71 &<TokenData<T>>::get((self.id, token_id))
87 }89 }
8890
89 fn total_supply(&self) -> Result<uint256> {91 fn total_supply(&self) -> Result<uint256> {
92 self.consume_store_reads(1)?;
90 Ok(<Pallet<T>>::total_supply(self).into())93 Ok(<Pallet<T>>::total_supply(self).into())
91 }94 }
92}95}
9396
94#[solidity_interface(name = "ERC721", events(ERC721Events))]97#[solidity_interface(name = "ERC721", events(ERC721Events))]
95impl<T: Config> NonfungibleHandle<T> {98impl<T: Config> NonfungibleHandle<T> {
96 fn balance_of(&self, owner: address) -> Result<uint256> {99 fn balance_of(&self, owner: address) -> Result<uint256> {
100 self.consume_store_reads(1)?;
97 let owner = T::CrossAccountId::from_eth(owner);101 let owner = T::CrossAccountId::from_eth(owner);
98 let balance = <AccountBalance<T>>::get((self.id, owner));102 let balance = <AccountBalance<T>>::get((self.id, owner));
99 Ok(balance.into())103 Ok(balance.into())
100 }104 }
101 fn owner_of(&self, token_id: uint256) -> Result<address> {105 fn owner_of(&self, token_id: uint256) -> Result<address> {
106 self.consume_store_reads(1)?;
102 let token: TokenId = token_id.try_into()?;107 let token: TokenId = token_id.try_into()?;
103 Ok(*<TokenData<T>>::get((self.id, token))108 Ok(*<TokenData<T>>::get((self.id, token))
104 .ok_or("token not found")?109 .ok_or("token not found")?
129 Err("not implemented".into())134 Err("not implemented".into())
130 }135 }
131136
137 #[weight(<SelfWeightOf<T>>::transfer_from())]
132 fn transfer_from(138 fn transfer_from(
133 &mut self,139 &mut self,
134 caller: caller,140 caller: caller,
147 Ok(())153 Ok(())
148 }154 }
149155
156 #[weight(<SelfWeightOf<T>>::approve())]
150 fn approve(157 fn approve(
151 &mut self,158 &mut self,
152 caller: caller,159 caller: caller,
189196
190#[solidity_interface(name = "ERC721Burnable")]197#[solidity_interface(name = "ERC721Burnable")]
191impl<T: Config> NonfungibleHandle<T> {198impl<T: Config> NonfungibleHandle<T> {
199 #[weight(<SelfWeightOf<T>>::burn_item())]
192 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {200 fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
193 let caller = T::CrossAccountId::from_eth(caller);201 let caller = T::CrossAccountId::from_eth(caller);
194 let token = token_id.try_into()?;202 let token = token_id.try_into()?;
206214
207 /// `token_id` should be obtained with `next_token_id` method,215 /// `token_id` should be obtained with `next_token_id` method,
208 /// unlike standard, you can't specify it manually216 /// unlike standard, you can't specify it manually
217 #[weight(<SelfWeightOf<T>>::create_item())]
209 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {218 fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
210 let caller = T::CrossAccountId::from_eth(caller);219 let caller = T::CrossAccountId::from_eth(caller);
211 let to = T::CrossAccountId::from_eth(to);220 let to = T::CrossAccountId::from_eth(to);
235 /// `token_id` should be obtained with `next_token_id` method,244 /// `token_id` should be obtained with `next_token_id` method,
236 /// unlike standard, you can't specify it manually245 /// unlike standard, you can't specify it manually
237 #[solidity(rename_selector = "mintWithTokenURI")]246 #[solidity(rename_selector = "mintWithTokenURI")]
247 #[weight(<SelfWeightOf<T>>::create_item())]
238 fn mint_with_token_uri(248 fn mint_with_token_uri(
239 &mut self,249 &mut self,
240 caller: caller,250 caller: caller,
276286
277#[solidity_interface(name = "ERC721UniqueExtensions")]287#[solidity_interface(name = "ERC721UniqueExtensions")]
278impl<T: Config> NonfungibleHandle<T> {288impl<T: Config> NonfungibleHandle<T> {
289 #[weight(<SelfWeightOf<T>>::transfer())]
279 fn transfer(290 fn transfer(
280 &mut self,291 &mut self,
281 caller: caller,292 caller: caller,
291 Ok(())302 Ok(())
292 }303 }
293304
305 #[weight(<SelfWeightOf<T>>::burn_from())]
294 fn burn_from(306 fn burn_from(
295 &mut self,307 &mut self,
296 caller: caller,308 caller: caller,
307 }319 }
308320
309 fn next_token_id(&self) -> Result<uint256> {321 fn next_token_id(&self) -> Result<uint256> {
322 self.consume_store_reads(1)?;
310 Ok(<TokensMinted<T>>::get(self.id)323 Ok(<TokensMinted<T>>::get(self.id)
311 .checked_add(1)324 .checked_add(1)
312 .ok_or("item id overflow")?325 .ok_or("item id overflow")?
313 .into())326 .into())
314 }327 }
315328
329 #[weight(<SelfWeightOf<T>>::set_variable_metadata(data.len() as u32))]
316 fn set_variable_metadata(330 fn set_variable_metadata(
317 &mut self,331 &mut self,
318 caller: caller,332 caller: caller,
328 }342 }
329343
330 fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {344 fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {
345 self.consume_store_reads(1)?;
331 let token: TokenId = token_id.try_into()?;346 let token: TokenId = token_id.try_into()?;
332347
333 Ok(<TokenData<T>>::get((self.id, token))348 Ok(<TokenData<T>>::get((self.id, token))
334 .ok_or("token not found")?349 .ok_or("token not found")?
335 .variable_data)350 .variable_data)
336 }351 }
337352
353 #[weight(<SelfWeightOf<T>>::create_multiple_items(token_ids.len() as u32))]
338 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {354 fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
339 let caller = T::CrossAccountId::from_eth(caller);355 let caller = T::CrossAccountId::from_eth(caller);
340 let to = T::CrossAccountId::from_eth(to);356 let to = T::CrossAccountId::from_eth(to);
363 }379 }
364380
365 #[solidity(rename_selector = "mintBulkWithTokenURI")]381 #[solidity(rename_selector = "mintBulkWithTokenURI")]
382 #[weight(<SelfWeightOf<T>>::create_multiple_items(tokens.len() as u32))]
366 fn mint_bulk_with_token_uri(383 fn mint_bulk_with_token_uri(
367 &mut self,384 &mut self,
368 caller: caller,385 caller: caller,
411impl<T: Config> NonfungibleHandle<T> {}428impl<T: Config> NonfungibleHandle<T> {}
412429
413// Not a tests, but code generators430// Not a tests, but code generators
414generate_stubgen!(gen_impl, UniqueNFTCall, true);431generate_stubgen!(gen_impl, UniqueNFTCall<()>, true);
415generate_stubgen!(gen_iface, UniqueNFTCall, false);432generate_stubgen!(gen_iface, UniqueNFTCall<()>, false);
416
417pub const CODE: &[u8] = include_bytes!("./stubs/UniqueNFT.raw");
418433
419impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {434impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {
420 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");435 const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");
421436
422 fn call(mut self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {437 fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {
423 let result = call_internal::<UniqueNFTCall, _>(*source, &mut self, value, input);438 call::<T, UniqueNFTCall<T>, _>(*source, self, value, input)
424 self.0.recorder.evm_to_precompile_output(result)
425 }439 }
426}440}
427441
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -8,6 +8,7 @@
 use pallet_common::{
 	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
 };
+use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
 use sp_core::H160;
 use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
 use sp_std::{vec::Vec, vec};
@@ -114,6 +115,14 @@
 		self.0
 	}
 }
+impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {
+	fn recorder(&self) -> &SubstrateRecorder<T> {
+		self.0.recorder()
+	}
+	fn into_recorder(self) -> SubstrateRecorder<T> {
+		self.0.into_recorder()
+	}
+}
 impl<T: Config> Deref for NonfungibleHandle<T> {
 	type Target = pallet_common::CollectionHandle<T>;
 
@@ -164,8 +173,7 @@
 			.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
 		ensure!(
 			&token_data.owner == sender
-				|| (collection.limits.owner_can_transfer()
-					&& collection.is_owner_or_admin(sender)?),
+				|| (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),
 			<CommonError<T>>::NoPermission
 		);
 
@@ -194,7 +202,7 @@
 			));
 		}
 
-		collection.log_infallible(ERC721Events::Transfer {
+		collection.log(ERC721Events::Transfer {
 			from: *token_data.owner.as_eth(),
 			to: H160::default(),
 			token_id: token.into(),
@@ -223,8 +231,7 @@
 			.ok_or_else(|| <CommonError<T>>::TokenNotFound)?;
 		ensure!(
 			&token_data.owner == from
-				|| (collection.limits.owner_can_transfer()
-					&& collection.is_owner_or_admin(from)?),
+				|| (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),
 			<CommonError<T>>::NoPermission
 		);
 
@@ -251,9 +258,6 @@
 		} else {
 			None
 		};
-
-		collection.consume_sstores(4)?;
-		collection.consume_log(3, 0)?;
 
 		// =========
 
@@ -278,7 +282,7 @@
 		}
 		Self::set_allowance_unchecked(collection, from, token, None, true);
 
-		collection.log_infallible(ERC721Events::Transfer {
+		collection.log(ERC721Events::Transfer {
 			from: *from.as_eth(),
 			to: *to.as_eth(),
 			token_id: token.into(),
@@ -298,8 +302,7 @@
 		sender: &T::CrossAccountId,
 		data: Vec<CreateItemData<T>>,
 	) -> DispatchResult {
-		let unrestricted_minting = collection.is_owner_or_admin(sender)?;
-		if !unrestricted_minting {
+		if !collection.is_owner_or_admin(sender) {
 			ensure!(
 				collection.mint_mode,
 				<CommonError<T>>::PublicMintingNotAllowed
@@ -313,14 +316,6 @@
 
 		for data in data.iter() {
 			<PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;
-			if !data.const_data.is_empty() {
-				collection.consume_sstore()?;
-			}
-			if !data.variable_data.is_empty() {
-				collection.consume_sstore()?;
-			}
-			collection.consume_sstore()?;
-			collection.consume_log(3, 0)?;
 		}
 
 		let first_token = <TokensMinted<T>>::get(collection.id);
@@ -331,7 +326,6 @@
 			tokens_minted < collection.limits.token_limit(),
 			<CommonError<T>>::CollectionTokenLimitExceeded
 		);
-		collection.consume_sstore()?;
 
 		let mut balances = BTreeMap::new();
 		for data in &data {
@@ -345,7 +339,6 @@
 				<CommonError<T>>::AccountTokenLimitExceeded,
 			);
 		}
-		collection.consume_sstores(balances.len())?;
 
 		// =========
 
@@ -366,7 +359,7 @@
 			);
 			<Owned<T>>::insert((collection.id, &data.owner, token), true);
 
-			collection.log_infallible(ERC721Events::Transfer {
+			collection.log(ERC721Events::Transfer {
 				from: H160::default(),
 				to: *data.owner.as_eth(),
 				token_id: token.into(),
@@ -393,7 +386,7 @@
 			<Allowance<T>>::insert((collection.id, token), spender);
 			// In ERC721 there is only one possible approved user of token, so we set
 			// approved user to spender
-			collection.log_infallible(ERC721Events::Approval {
+			collection.log(ERC721Events::Approval {
 				owner: *sender.as_eth(),
 				approved: *spender.as_eth(),
 				token_id: token.into(),
@@ -423,7 +416,7 @@
 			if !assume_implicit_eth {
 				// In ERC721 there is only one possible approved user of token, so we set
 				// approved user to zero address
-				collection.log_infallible(ERC721Events::Approval {
+				collection.log(ERC721Events::Approval {
 					owner: *sender.as_eth(),
 					approved: H160::default(),
 					token_id: token.into(),
@@ -463,7 +456,7 @@
 			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
 		if &token_data.owner != sender {
 			ensure!(
-				collection.ignores_owned_amount(sender)?,
+				collection.ignores_owned_amount(sender),
 				<CommonError<T>>::CantApproveMoreThanOwned
 			);
 		}
@@ -491,7 +484,7 @@
 
 		if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
@@ -519,7 +512,7 @@
 
 		if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
@@ -542,8 +535,6 @@
 		let token_data =
 			<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
 		collection.check_can_update_meta(sender, &token_data.owner)?;
-
-		collection.consume_sstore()?;
 
 		// =========
 
modifiedpallets/refungible/src/common.rsdiffbeforeafterboth
--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -58,7 +58,7 @@
 	}
 
 	fn burn_from() -> Weight {
-		0
+		<SelfWeightOf<T>>::burn_from()
 	}
 
 	fn set_variable_metadata(bytes: u32) -> Weight {
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -359,8 +359,7 @@
 		sender: &T::CrossAccountId,
 		data: Vec<CreateItemData<T>>,
 	) -> DispatchResult {
-		let unrestricted_minting = collection.is_owner_or_admin(sender)?;
-		if !unrestricted_minting {
+		if !collection.is_owner_or_admin(sender) {
 			ensure!(
 				collection.mint_mode,
 				<CommonError<T>>::PublicMintingNotAllowed
@@ -492,7 +491,7 @@
 
 		if <Balance<T>>::get((collection.id, token, sender)) < amount {
 			ensure!(
-				collection.ignores_owned_amount(sender)? && Self::token_exists(collection, token),
+				collection.ignores_owned_amount(sender) && Self::token_exists(collection, token),
 				<CommonError<T>>::CantApproveMoreThanOwned
 			);
 		}
@@ -523,7 +522,7 @@
 			<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);
 		if allowance.is_none() {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
@@ -556,7 +555,7 @@
 			<Allowance<T>>::get((collection.id, token, from, &spender)).checked_sub(amount);
 		if allowance.is_none() {
 			ensure!(
-				collection.ignores_allowance(spender)?,
+				collection.ignores_allowance(spender),
 				<CommonError<T>>::TokenValueNotEnough
 			);
 		}
@@ -585,7 +584,6 @@
 			&T::CrossAccountId::from_sub(collection.owner.clone()),
 		)?;
 
-		collection.consume_sstore()?;
 		let token_data = <TokenData<T>>::get((collection.id, token));
 
 		// =========
modifiedruntime/Cargo.tomldiffbeforeafterboth
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -387,10 +387,10 @@
 pallet-evm-transaction-payment = { path = '../pallets/evm-transaction-payment', default-features = false }
 pallet-evm-coder-substrate = { default-features = false, path = "../pallets/evm-coder-substrate" }
 
-pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
-fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12" }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+pallet-ethereum = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-rpc = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
+fp-self-contained = { default-features = false, git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.12-weights" }
 
 ################################################################################
 # Build Dependencies
modifiedruntime/src/lib.rsdiffbeforeafterboth
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -66,7 +66,7 @@
 };
 use smallvec::smallvec;
 use codec::{Encode, Decode};
-use pallet_evm::{Account as EVMAccount, FeeCalculator, OnMethodCall};
+use pallet_evm::{Account as EVMAccount, FeeCalculator, GasWeightMapping, OnMethodCall};
 use fp_rpc::TransactionStatus;
 use sp_core::crypto::Public;
 use sp_runtime::{
@@ -247,10 +247,37 @@
 	}
 }
 
+// Assuming slowest ethereum opcode is SSTORE, with gas price of 20000 as our worst case
+// (contract, which only writes a lot of data),
+// approximating on top of our real store write weight
+parameter_types! {
+	pub const WritesPerSecond: u64 = WEIGHT_PER_SECOND / <Runtime as frame_system::Config>::DbWeight::get().write;
+	pub const GasPerSecond: u64 = WritesPerSecond::get() * 20000;
+	pub const WeightPerGas: u64 = WEIGHT_PER_SECOND / GasPerSecond::get();
+}
+
+/// Limiting EVM execution to 50% of block for substrate users and management tasks
+/// EVM transaction consumes more weight than substrate's, so we can't rely on them being
+/// scheduled fairly
+const EVM_DISPATCH_RATIO: Perbill = Perbill::from_percent(50);
+parameter_types! {
+	pub BlockGasLimit: U256 = U256::from(NORMAL_DISPATCH_RATIO * EVM_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT / WeightPerGas::get());
+}
+
+pub enum FixedGasWeightMapping {}
+impl GasWeightMapping for FixedGasWeightMapping {
+	fn gas_to_weight(gas: u64) -> Weight {
+		gas.saturating_mul(WeightPerGas::get())
+	}
+	fn weight_to_gas(weight: Weight) -> u64 {
+		weight / WeightPerGas::get()
+	}
+}
+
 impl pallet_evm::Config for Runtime {
 	type BlockGasLimit = BlockGasLimit;
 	type FeeCalculator = FixedFee;
-	type GasWeightMapping = ();
+	type GasWeightMapping = FixedGasWeightMapping;
 	type BlockHashMapping = pallet_ethereum::EthereumBlockHashMapping<Self>;
 	type CallOrigin = EnsureAddressTruncated;
 	type WithdrawOrigin = EnsureAddressTruncated;
@@ -289,14 +316,9 @@
 	}
 }
 
-parameter_types! {
-	pub BlockGasLimit: U256 = U256::from(u32::max_value());
-}
-
 impl pallet_ethereum::Config for Runtime {
 	type Event = Event;
 	type StateRoot = pallet_ethereum::IntermediateStateRoot;
-	type EvmSubmitLog = pallet_evm::Pallet<Self>;
 }
 
 impl pallet_randomness_collective_flip::Config for Runtime {}
@@ -671,6 +693,7 @@
 
 impl pallet_evm_coder_substrate::Config for Runtime {
 	type EthereumTransactionSender = pallet_ethereum::Pallet<Self>;
+	type GasWeightMapping = FixedGasWeightMapping;
 }
 
 impl pallet_xcm::Config for Runtime {