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

difftreelog

feat calculate signature in compile time

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

11 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1096,6 +1096,26 @@
 checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3"
 
 [[package]]
+name = "const_format"
+version = "0.2.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "939dc9e2eb9077e0679d2ce32de1ded8531779360b003b4a972a7a39ec263495"
+dependencies = [
+ "const_format_proc_macros",
+]
+
+[[package]]
+name = "const_format_proc_macros"
+version = "0.2.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
 name = "constant_time_eq"
 version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2353,6 +2373,7 @@
 version = "0.1.3"
 dependencies = [
  "concat-idents",
+ "const_format",
  "ethereum",
  "evm-coder-procedural",
  "evm-core 0.35.0 (git+https://github.com/uniquenetwork/evm?branch=unique-polkadot-v0.9.30)",
@@ -2362,6 +2383,7 @@
  "impl-trait-for-tuples",
  "pallet-evm 6.0.0-dev (git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.27-fee-limit)",
  "primitive-types",
+ "sha3-const",
  "similar-asserts",
  "sp-std 4.0.0 (git+https://github.com/paritytech/substrate?branch=polkadot-v0.9.30)",
 ]
@@ -11085,6 +11107,12 @@
 ]
 
 [[package]]
+name = "sha3-const"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af9625d558d174dbdc711248b479271c0fdca39e9d3d67f6e890b3d4251fbf8e"
+
+[[package]]
 name = "sharded-slab"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
modifiedcrates/evm-coder/Cargo.tomldiffbeforeafterboth
--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -5,6 +5,10 @@
 edition = "2021"
 
 [dependencies]
+const_format = { version = "0.2.26", default-features = false }
+sha3-const = { version = "0.1.0", default-features = false }
+# Ethereum uses keccak (=sha3) for selectors
+# sha3 = "0.10.1"
 # evm-coder reexports those proc-macro
 evm-coder-procedural = { path = "./procedural" }
 # Evm uses primitive-types for H160, H256 and others
modifiedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -20,6 +20,7 @@
 // about Procedural Macros in Rust book:
 // https://doc.rust-lang.org/reference/procedural-macros.html
 
+use proc_macro2::{TokenStream, Group};
 use quote::{quote, ToTokens};
 use inflector::cases;
 use std::fmt::Write;
@@ -328,6 +329,7 @@
 	}
 }
 
+#[derive(Debug)]
 enum AbiType {
 	// type
 	Plain(Ident),
@@ -473,6 +475,7 @@
 	}
 }
 
+#[derive(Debug)]
 struct MethodArg {
 	name: Ident,
 	camel_name: String,
@@ -722,13 +725,25 @@
 		}
 	}
 
+	fn expand_selector(&self) -> proc_macro2::TokenStream {
+		let custom_signature = self.expand_custom_signature();
+		quote! {
+			 {
+				let a = ::evm_coder::sha3_const::Keccak256::new()
+					.update(#custom_signature.as_bytes())
+					.finalize();
+				[a[0], a[1], a[2], a[3]]
+			}
+		}
+	}
+
 	fn expand_const(&self) -> proc_macro2::TokenStream {
 		let screaming_name = &self.screaming_name;
-		let selector = u32::to_be_bytes(self.selector);
 		let selector_str = &self.selector_str;
+		let selector = &self.expand_selector();
 		quote! {
 			#[doc = #selector_str]
-			const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];
+			const #screaming_name: ::evm_coder::types::bytes4 = #selector;
 		}
 	}
 
@@ -831,6 +846,59 @@
 		}
 	}
 
+	fn expand_custom_signature(&self) -> proc_macro2::TokenStream {
+		let mut first_comma = true;
+		let mut custom_signature = TokenStream::new();
+		let mut template = self.camel_name.clone() + "(";
+		self.args
+			.iter()
+			.filter_map(|a| {
+				if a.is_special() {
+					return None;
+				};
+
+				match a.ty {
+					AbiType::Plain(ref ident) => Some(ident),
+					_ => None,
+				}
+			})
+			.for_each(|ident| {
+				if !first_comma {
+					custom_signature.extend(quote!(,));
+					template.push(',');
+				} else {
+					first_comma = false;
+				};
+				template.push_str("{}");
+				let ident_str = ident.to_string();
+				match ident_str.as_str() {
+					"address" | "uint8" | "uint16" | "uint32" | "uint64" | "uint128"
+					| "uint256" | "bytes4" | "topic" | "string" | "bytes" | "void" | "caller"
+					| "bool" | "" => {
+						custom_signature.extend(quote!(#ident_str));
+					}
+					_ => {
+						custom_signature.extend(quote! {
+							#ident::SIGNATURE_STRING
+						});
+					}
+				}
+			});
+
+		template.push(')');
+		let mut template = quote!(#template);
+		template.extend(quote!(,));
+		template.extend(custom_signature);
+		let custom_signature_group = Group::new(proc_macro2::Delimiter::Parenthesis, template);
+		let mut custom_signature = quote! {
+			::evm_coder::const_format::formatcp!
+		};
+		custom_signature.extend(custom_signature_group.to_token_stream());
+
+		// println!("!!!!! {}", custom_signature);
+		custom_signature
+	}
+
 	fn expand_solidity_function(&self) -> proc_macro2::TokenStream {
 		let camel_name = &self.camel_name;
 		let mutability = match self.mutability {
@@ -847,15 +915,17 @@
 			.map(MethodArg::expand_solidity_argument);
 		let docs = &self.docs;
 		let selector_str = &self.selector_str;
-		let selector = self.selector;
+		let selector = &self.expand_selector();
 		let hide = self.hide;
+		let custom_signature = self.expand_custom_signature();
 		let is_payable = self.has_value_args;
 		quote! {
 			SolidityFunction {
 				docs: &[#(#docs),*],
 				selector_str: #selector_str,
-				selector: #selector,
 				hide: #hide,
+				selector: u32::from_be_bytes(#selector),
+				custom_signature: #custom_signature,
 				name: #camel_name,
 				mutability: #mutability,
 				is_payable: #is_payable,
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -90,6 +90,8 @@
 pub use evm_coder_procedural::solidity;
 /// See [`solidity_interface`]
 pub use evm_coder_procedural::weight;
+pub use const_format;
+pub use sha3_const;
 
 /// Derives [`ToLog`] for enum
 ///
@@ -227,6 +229,14 @@
 		}
 	}
 
+	impl SignatureString for EthCrossAccount {
+		const SIGNATURE_STRING: &'static str = "(address,uint256)";
+	}
+
+	pub trait SignatureString {
+		const SIGNATURE_STRING: &'static str;
+	}
+
 	/// Convert `CrossAccountId` to `uint256`.
 	pub fn convert_cross_account_to_uint256<T: pallet_evm::account::Config>(
 		from: &T::CrossAccountId,
@@ -348,6 +358,18 @@
 		assert_eq!(fn_selector!(transfer(address, uint256)), 0xa9059cbb);
 	}
 
+	// #[test]
+	// fn function_selector_generation_1() {
+	// 	assert_eq!(
+	// 		fn_selector!(transferFromCrossAccountToCrossAccount(
+	// 			EthCrossAccount,
+	// 			EthCrossAccount,
+	// 			uint256
+	// 		)),
+	// 		2543295963
+	// 	);
+	// }
+
 	#[test]
 	fn event_topic_generation() {
 		assert_eq!(
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -35,6 +35,12 @@
 use crate::types::*;
 
 #[derive(Default)]
+pub struct FunctionSelectorMaker {
+	pub name: string,
+	pub args: Vec<fn() -> string>,
+}
+
+#[derive(Default)]
 pub struct TypeCollector {
 	/// Code => id
 	/// id ordering is required to perform topo-sort on the resulting data
@@ -482,11 +488,25 @@
 	View,
 	Mutable,
 }
+
+// fn fn_selector_str(input: &str) -> u32 {
+// 	use sha3::Digest;
+// 	let mut hasher = sha3::Keccak256::new();
+// 	hasher.update(input.as_bytes());
+// 	let result = hasher.finalize();
+
+// 	let mut selector_bytes = [0; 4];
+// 	selector_bytes.copy_from_slice(&result[0..4]);
+
+// 	u32::from_be_bytes(selector_bytes)
+// }
+
 pub struct SolidityFunction<A, R> {
 	pub docs: &'static [&'static str],
 	pub selector_str: &'static str,
 	pub selector: u32,
 	pub hide: bool,
+	pub custom_signature: &'static str,
 	pub name: &'static str,
 	pub args: A,
 	pub result: R,
@@ -512,7 +532,7 @@
 		writeln!(
 			writer,
 			"\t{hide_comment}///  or in textual repr: {}",
-			self.selector_str
+			self.custom_signature
 		)?;
 		write!(writer, "\t{hide_comment}function {}(", self.name)?;
 		self.args.solidity_name(writer, tc)?;
modifiedpallets/evm-contract-helpers/Cargo.tomldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/Cargo.toml
+++ b/pallets/evm-contract-helpers/Cargo.toml
@@ -27,7 +27,7 @@
 evm-coder = { default-features = false, path = '../../crates/evm-coder' }
 pallet-common = { default-features = false, path = '../../pallets/common' }
 pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
-pallet-evm-transaction-payment = {  default-features = false, path = '../../pallets/evm-transaction-payment' }
+pallet-evm-transaction-payment = { default-features = false, path = '../../pallets/evm-transaction-payment' }
 up-data-structs = { default-features = false, path = '../../primitives/data-structs', features = [
     'serde1',
 ] }
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -16,6 +16,8 @@
 
 //! Implementation of magic contract
 
+extern crate alloc;
+use alloc::{format, string::ToString};
 use core::marker::PhantomData;
 use evm_coder::{
 	abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*, ToLog,
modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
before · pallets/fungible/src/erc.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! ERC-20 standart support implementation.1819use core::char::{REPLACEMENT_CHARACTER, decode_utf16};20use core::convert::TryInto;21use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};22use pallet_common::eth::convert_tuple_to_cross_account;23use up_data_structs::CollectionMode;24use pallet_common::erc::{CommonEvmHandler, PrecompileResult};25use sp_std::vec::Vec;26use pallet_evm::{account::CrossAccountId, PrecompileHandle};27use pallet_evm_coder_substrate::{call, dispatch_to_evm};28use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};29use pallet_common::{CollectionHandle, erc::CollectionCall};3031use crate::{32	Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,33	weights::WeightInfo,34};3536#[derive(ToLog)]37pub enum ERC20Events {38	Transfer {39		#[indexed]40		from: address,41		#[indexed]42		to: address,43		value: uint256,44	},45	Approval {46		#[indexed]47		owner: address,48		#[indexed]49		spender: address,50		value: uint256,51	},52}5354#[solidity_interface(name = ERC20, events(ERC20Events))]55impl<T: Config> FungibleHandle<T> {56	fn name(&self) -> Result<string> {57		Ok(decode_utf16(self.name.iter().copied())58			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))59			.collect::<string>())60	}61	fn symbol(&self) -> Result<string> {62		Ok(string::from_utf8_lossy(&self.token_prefix).into())63	}64	fn total_supply(&self) -> Result<uint256> {65		self.consume_store_reads(1)?;66		Ok(<TotalSupply<T>>::get(self.id).into())67	}6869	fn decimals(&self) -> Result<uint8> {70		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {71			*decimals72		} else {73			unreachable!()74		})75	}76	fn balance_of(&self, owner: address) -> Result<uint256> {77		self.consume_store_reads(1)?;78		let owner = T::CrossAccountId::from_eth(owner);79		let balance = <Balance<T>>::get((self.id, owner));80		Ok(balance.into())81	}82	#[weight(<SelfWeightOf<T>>::transfer())]83	fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {84		let caller = T::CrossAccountId::from_eth(caller);85		let to = T::CrossAccountId::from_eth(to);86		let amount = amount.try_into().map_err(|_| "amount overflow")?;87		let budget = self88			.recorder89			.weight_calls_budget(<StructureWeight<T>>::find_parent());9091		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;92		Ok(true)93	}94	#[weight(<SelfWeightOf<T>>::transfer_from())]95	fn transfer_from(96		&mut self,97		caller: caller,98		from: address,99		to: address,100		amount: uint256,101	) -> Result<bool> {102		let caller = T::CrossAccountId::from_eth(caller);103		let from = T::CrossAccountId::from_eth(from);104		let to = T::CrossAccountId::from_eth(to);105		let amount = amount.try_into().map_err(|_| "amount overflow")?;106		let budget = self107			.recorder108			.weight_calls_budget(<StructureWeight<T>>::find_parent());109110		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)111			.map_err(dispatch_to_evm::<T>)?;112		Ok(true)113	}114	#[weight(<SelfWeightOf<T>>::approve())]115	fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {116		let caller = T::CrossAccountId::from_eth(caller);117		let spender = T::CrossAccountId::from_eth(spender);118		let amount = amount.try_into().map_err(|_| "amount overflow")?;119120		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)121			.map_err(dispatch_to_evm::<T>)?;122		Ok(true)123	}124	fn allowance(&self, owner: address, spender: address) -> Result<uint256> {125		self.consume_store_reads(1)?;126		let owner = T::CrossAccountId::from_eth(owner);127		let spender = T::CrossAccountId::from_eth(spender);128129		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())130	}131}132133#[solidity_interface(name = ERC20Mintable)]134impl<T: Config> FungibleHandle<T> {135	/// Mint tokens for `to` account.136	/// @param to account that will receive minted tokens137	/// @param amount amount of tokens to mint138	#[weight(<SelfWeightOf<T>>::create_item())]139	fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {140		let caller = T::CrossAccountId::from_eth(caller);141		let to = T::CrossAccountId::from_eth(to);142		let amount = amount.try_into().map_err(|_| "amount overflow")?;143		let budget = self144			.recorder145			.weight_calls_budget(<StructureWeight<T>>::find_parent());146		<Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)147			.map_err(dispatch_to_evm::<T>)?;148		Ok(true)149	}150}151152#[solidity_interface(name = ERC20UniqueExtensions)]153impl<T: Config> FungibleHandle<T>154where155	T::AccountId: From<[u8; 32]>,156{157	#[weight(<SelfWeightOf<T>>::approve())]158	fn approve_cross(159		&mut self,160		caller: caller,161		spender: (address, uint256),162		amount: uint256,163	) -> Result<bool> {164		let caller = T::CrossAccountId::from_eth(caller);165		let spender = convert_tuple_to_cross_account::<T>(spender)?;166		let amount = amount.try_into().map_err(|_| "amount overflow")?;167168		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)169			.map_err(dispatch_to_evm::<T>)?;170		Ok(true)171	}172173	/// Burn tokens from account174	/// @dev Function that burns an `amount` of the tokens of a given account,175	/// deducting from the sender's allowance for said account.176	/// @param from The account whose tokens will be burnt.177	/// @param amount The amount that will be burnt.178	#[weight(<SelfWeightOf<T>>::burn_from())]179	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {180		let caller = T::CrossAccountId::from_eth(caller);181		let from = T::CrossAccountId::from_eth(from);182		let amount = amount.try_into().map_err(|_| "amount overflow")?;183		let budget = self184			.recorder185			.weight_calls_budget(<StructureWeight<T>>::find_parent());186187		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)188			.map_err(dispatch_to_evm::<T>)?;189		Ok(true)190	}191192	/// Burn tokens from account193	/// @dev Function that burns an `amount` of the tokens of a given account,194	/// deducting from the sender's allowance for said account.195	/// @param from The account whose tokens will be burnt.196	/// @param amount The amount that will be burnt.197	#[weight(<SelfWeightOf<T>>::burn_from())]198	fn burn_from_cross(199		&mut self,200		caller: caller,201		from: (address, uint256),202		amount: uint256,203	) -> Result<bool> {204		let caller = T::CrossAccountId::from_eth(caller);205		let from = convert_tuple_to_cross_account::<T>(from)?;206		let amount = amount.try_into().map_err(|_| "amount overflow")?;207		let budget = self208			.recorder209			.weight_calls_budget(<StructureWeight<T>>::find_parent());210211		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)212			.map_err(dispatch_to_evm::<T>)?;213		Ok(true)214	}215216	/// Mint tokens for multiple accounts.217	/// @param amounts array of pairs of account address and amount218	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]219	fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {220		let caller = T::CrossAccountId::from_eth(caller);221		let budget = self222			.recorder223			.weight_calls_budget(<StructureWeight<T>>::find_parent());224		let amounts = amounts225			.into_iter()226			.map(|(to, amount)| {227				Ok((228					T::CrossAccountId::from_eth(to),229					amount.try_into().map_err(|_| "amount overflow")?,230				))231			})232			.collect::<Result<_>>()?;233234		<Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)235			.map_err(dispatch_to_evm::<T>)?;236		Ok(true)237	}238239	#[weight(<SelfWeightOf<T>>::transfer_from())]240	fn transfer_from_cross(241		&mut self,242		caller: caller,243		from: (address, uint256),244		to: (address, uint256),245		amount: uint256,246	) -> Result<bool> {247		let caller = T::CrossAccountId::from_eth(caller);248		let from = convert_tuple_to_cross_account::<T>(from)?;249		let to = convert_tuple_to_cross_account::<T>(to)?;250		let amount = amount.try_into().map_err(|_| "amount overflow")?;251		let budget = self252			.recorder253			.weight_calls_budget(<StructureWeight<T>>::find_parent());254255		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)256			.map_err(dispatch_to_evm::<T>)?;257		Ok(true)258	}259}260261#[solidity_interface(262	name = UniqueFungible,263	is(264		ERC20,265		ERC20Mintable,266		ERC20UniqueExtensions,267		Collection(via(common_mut returns CollectionHandle<T>)),268	)269)]270impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}271272generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);273generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);274275impl<T: Config> CommonEvmHandler for FungibleHandle<T>276where277	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,278{279	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");280281	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {282		call::<T, UniqueFungibleCall<T>, _, _>(handle, self)283	}284}
after · pallets/fungible/src/erc.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! ERC-20 standart support implementation.1819extern crate alloc;20use alloc::{format, string::ToString};21use core::char::{REPLACEMENT_CHARACTER, decode_utf16};22use core::convert::TryInto;23use evm_coder::{ToLog, execution::*, generate_stubgen, solidity_interface, types::*, weight};24use pallet_common::eth::convert_tuple_to_cross_account;25use up_data_structs::CollectionMode;26use pallet_common::erc::{CommonEvmHandler, PrecompileResult};27use sp_std::vec::Vec;28use pallet_evm::{account::CrossAccountId, PrecompileHandle};29use pallet_evm_coder_substrate::{call, dispatch_to_evm};30use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};31use pallet_common::{CollectionHandle, erc::CollectionCall};3233use crate::{34	Allowance, Balance, Config, FungibleHandle, Pallet, SelfWeightOf, TotalSupply,35	weights::WeightInfo,36};3738#[derive(ToLog)]39pub enum ERC20Events {40	Transfer {41		#[indexed]42		from: address,43		#[indexed]44		to: address,45		value: uint256,46	},47	Approval {48		#[indexed]49		owner: address,50		#[indexed]51		spender: address,52		value: uint256,53	},54}5556#[solidity_interface(name = ERC20, events(ERC20Events))]57impl<T: Config> FungibleHandle<T> {58	fn name(&self) -> Result<string> {59		Ok(decode_utf16(self.name.iter().copied())60			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))61			.collect::<string>())62	}63	fn symbol(&self) -> Result<string> {64		Ok(string::from_utf8_lossy(&self.token_prefix).into())65	}66	fn total_supply(&self) -> Result<uint256> {67		self.consume_store_reads(1)?;68		Ok(<TotalSupply<T>>::get(self.id).into())69	}7071	fn decimals(&self) -> Result<uint8> {72		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {73			*decimals74		} else {75			unreachable!()76		})77	}78	fn balance_of(&self, owner: address) -> Result<uint256> {79		self.consume_store_reads(1)?;80		let owner = T::CrossAccountId::from_eth(owner);81		let balance = <Balance<T>>::get((self.id, owner));82		Ok(balance.into())83	}84	#[weight(<SelfWeightOf<T>>::transfer())]85	fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {86		let caller = T::CrossAccountId::from_eth(caller);87		let to = T::CrossAccountId::from_eth(to);88		let amount = amount.try_into().map_err(|_| "amount overflow")?;89		let budget = self90			.recorder91			.weight_calls_budget(<StructureWeight<T>>::find_parent());9293		<Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;94		Ok(true)95	}96	#[weight(<SelfWeightOf<T>>::transfer_from())]97	fn transfer_from(98		&mut self,99		caller: caller,100		from: address,101		to: address,102		amount: uint256,103	) -> Result<bool> {104		let caller = T::CrossAccountId::from_eth(caller);105		let from = T::CrossAccountId::from_eth(from);106		let to = T::CrossAccountId::from_eth(to);107		let amount = amount.try_into().map_err(|_| "amount overflow")?;108		let budget = self109			.recorder110			.weight_calls_budget(<StructureWeight<T>>::find_parent());111112		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)113			.map_err(dispatch_to_evm::<T>)?;114		Ok(true)115	}116	#[weight(<SelfWeightOf<T>>::approve())]117	fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {118		let caller = T::CrossAccountId::from_eth(caller);119		let spender = T::CrossAccountId::from_eth(spender);120		let amount = amount.try_into().map_err(|_| "amount overflow")?;121122		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)123			.map_err(dispatch_to_evm::<T>)?;124		Ok(true)125	}126	fn allowance(&self, owner: address, spender: address) -> Result<uint256> {127		self.consume_store_reads(1)?;128		let owner = T::CrossAccountId::from_eth(owner);129		let spender = T::CrossAccountId::from_eth(spender);130131		Ok(<Allowance<T>>::get((self.id, owner, spender)).into())132	}133}134135#[solidity_interface(name = ERC20Mintable)]136impl<T: Config> FungibleHandle<T> {137	/// Mint tokens for `to` account.138	/// @param to account that will receive minted tokens139	/// @param amount amount of tokens to mint140	#[weight(<SelfWeightOf<T>>::create_item())]141	fn mint(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {142		let caller = T::CrossAccountId::from_eth(caller);143		let to = T::CrossAccountId::from_eth(to);144		let amount = amount.try_into().map_err(|_| "amount overflow")?;145		let budget = self146			.recorder147			.weight_calls_budget(<StructureWeight<T>>::find_parent());148		<Pallet<T>>::create_item(&self, &caller, (to, amount), &budget)149			.map_err(dispatch_to_evm::<T>)?;150		Ok(true)151	}152}153154#[solidity_interface(name = ERC20UniqueExtensions)]155impl<T: Config> FungibleHandle<T>156where157	T::AccountId: From<[u8; 32]>,158{159	#[weight(<SelfWeightOf<T>>::approve())]160	fn approve_cross(161		&mut self,162		caller: caller,163		spender: (address, uint256),164		amount: uint256,165	) -> Result<bool> {166		let caller = T::CrossAccountId::from_eth(caller);167		let spender = convert_tuple_to_cross_account::<T>(spender)?;168		let amount = amount.try_into().map_err(|_| "amount overflow")?;169170		<Pallet<T>>::set_allowance(self, &caller, &spender, amount)171			.map_err(dispatch_to_evm::<T>)?;172		Ok(true)173	}174175	/// Burn tokens from account176	/// @dev Function that burns an `amount` of the tokens of a given account,177	/// deducting from the sender's allowance for said account.178	/// @param from The account whose tokens will be burnt.179	/// @param amount The amount that will be burnt.180	#[weight(<SelfWeightOf<T>>::burn_from())]181	fn burn_from(&mut self, caller: caller, from: address, amount: uint256) -> Result<bool> {182		let caller = T::CrossAccountId::from_eth(caller);183		let from = T::CrossAccountId::from_eth(from);184		let amount = amount.try_into().map_err(|_| "amount overflow")?;185		let budget = self186			.recorder187			.weight_calls_budget(<StructureWeight<T>>::find_parent());188189		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)190			.map_err(dispatch_to_evm::<T>)?;191		Ok(true)192	}193194	/// Burn tokens from account195	/// @dev Function that burns an `amount` of the tokens of a given account,196	/// deducting from the sender's allowance for said account.197	/// @param from The account whose tokens will be burnt.198	/// @param amount The amount that will be burnt.199	#[weight(<SelfWeightOf<T>>::burn_from())]200	fn burn_from_cross(201		&mut self,202		caller: caller,203		from: (address, uint256),204		amount: uint256,205	) -> Result<bool> {206		let caller = T::CrossAccountId::from_eth(caller);207		let from = convert_tuple_to_cross_account::<T>(from)?;208		let amount = amount.try_into().map_err(|_| "amount overflow")?;209		let budget = self210			.recorder211			.weight_calls_budget(<StructureWeight<T>>::find_parent());212213		<Pallet<T>>::burn_from(self, &caller, &from, amount, &budget)214			.map_err(dispatch_to_evm::<T>)?;215		Ok(true)216	}217218	/// Mint tokens for multiple accounts.219	/// @param amounts array of pairs of account address and amount220	#[weight(<SelfWeightOf<T>>::create_multiple_items_ex(amounts.len() as u32))]221	fn mint_bulk(&mut self, caller: caller, amounts: Vec<(address, uint256)>) -> Result<bool> {222		let caller = T::CrossAccountId::from_eth(caller);223		let budget = self224			.recorder225			.weight_calls_budget(<StructureWeight<T>>::find_parent());226		let amounts = amounts227			.into_iter()228			.map(|(to, amount)| {229				Ok((230					T::CrossAccountId::from_eth(to),231					amount.try_into().map_err(|_| "amount overflow")?,232				))233			})234			.collect::<Result<_>>()?;235236		<Pallet<T>>::create_multiple_items(&self, &caller, amounts, &budget)237			.map_err(dispatch_to_evm::<T>)?;238		Ok(true)239	}240241	#[weight(<SelfWeightOf<T>>::transfer_from())]242	fn transfer_from_cross(243		&mut self,244		caller: caller,245		from: (address, uint256),246		to: (address, uint256),247		amount: uint256,248	) -> Result<bool> {249		let caller = T::CrossAccountId::from_eth(caller);250		let from = convert_tuple_to_cross_account::<T>(from)?;251		let to = convert_tuple_to_cross_account::<T>(to)?;252		let amount = amount.try_into().map_err(|_| "amount overflow")?;253		let budget = self254			.recorder255			.weight_calls_budget(<StructureWeight<T>>::find_parent());256257		<Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)258			.map_err(dispatch_to_evm::<T>)?;259		Ok(true)260	}261}262263#[solidity_interface(264	name = UniqueFungible,265	is(266		ERC20,267		ERC20Mintable,268		ERC20UniqueExtensions,269		Collection(via(common_mut returns CollectionHandle<T>)),270	)271)]272impl<T: Config> FungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}273274generate_stubgen!(gen_impl, UniqueFungibleCall<()>, true);275generate_stubgen!(gen_iface, UniqueFungibleCall<()>, false);276277impl<T: Config> CommonEvmHandler for FungibleHandle<T>278where279	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,280{281	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueFungible.raw");282283	fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {284		call::<T, UniqueFungibleCall<T>, _, _>(handle, self)285	}286}
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -20,6 +20,7 @@
 //! Method implementations are mostly doing parameter conversion and calling Nonfungible Pallet methods.
 
 extern crate alloc;
+use alloc::{format, string::ToString};
 use core::{
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,
modifiedpallets/refungible/Cargo.tomldiffbeforeafterboth
--- a/pallets/refungible/Cargo.toml
+++ b/pallets/refungible/Cargo.toml
@@ -26,7 +26,9 @@
 struct-versioning = { path = "../../crates/struct-versioning" }
 up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
 ethereum = { version = "0.12.0", default-features = false }
-scale-info = { version = "2.0.1", default-features = false, features = ["derive",] }
+scale-info = { version = "2.0.1", default-features = false, features = [
+    "derive",
+] }
 derivative = { version = "2.2.0", features = ["use_core"] }
 
 [features]
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -21,6 +21,7 @@
 
 extern crate alloc;
 
+use alloc::{format, string::ToString};
 use core::{
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,