git.delta.rocks / unique-network / refs/commits / 5fc39c3d8a70

difftreelog

refactor rewrite nft pallet to evm helpers

Yaroslav Bolyukin2021-07-27parent: #b905ada.patch.diff
in: master

7 files changed

modifiedpallets/nft/Cargo.tomldiffbeforeafterboth
--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -39,7 +39,7 @@
 
     'primitive-types/std',
     'evm-coder/std',
-    'evm-coder-substrate/std',
+    'pallet-evm-coder-substrate/std',
 ]
 
 ################################################################################
@@ -144,10 +144,12 @@
 sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" }
 
 evm-coder = { default-features = false, path = "../../crates/evm-coder" }
-evm-coder-substrate = { default-features = false, path = "../../crates/evm-coder-substrate" }
-primitive-types = { version = "0.9.0", default-features = false, features = ["serde_no_std"] }
+pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }
+primitive-types = { version = "0.9.0", default-features = false, features = [
+    "serde_no_std",
+] }
 
 pallet-evm = { default-features = false, version = "5.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
 pallet-ethereum = { default-features = false, version = "3.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
 fp-evm = { default-features = false, version = '2.0.0', git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" }
-hex-literal = "0.3.1"
\ No newline at end of file
+hex-literal = "0.3.1"
modifiedpallets/nft/src/eth/erc.rsdiffbeforeafterboth
before · pallets/nft/src/eth/erc.rs
1use evm_coder::{solidity_interface, solidity, types::*, ToLog};2use sp_std::vec::Vec;34#[solidity_interface]5pub trait InlineNameSymbol {6	type Error;78	fn name(&self) -> Result<string, Self::Error>;9	fn symbol(&self) -> Result<string, Self::Error>;10}1112#[solidity_interface]13pub trait InlineTotalSupply {14	type Error;1516	fn total_supply(&self) -> Result<uint256, Self::Error>;17}1819#[solidity_interface]20pub trait ERC165 {21	type Error;2223	fn supports_interface(&self, interface_id: bytes4) -> Result<bool, Self::Error>;24}2526#[solidity_interface(inline_is(InlineNameSymbol))]27pub trait ERC721Metadata {28	type Error;2930	#[solidity(rename_selector = "tokenURI")]31	fn token_uri(&self, token_id: uint256) -> Result<string, Self::Error>;32}3334#[solidity_interface(inline_is(InlineTotalSupply))]35pub trait ERC721Enumerable {36	type Error;3738	fn token_by_index(&self, index: uint256) -> Result<uint256, Self::Error>;39	fn token_of_owner_by_index(40		&self,41		owner: address,42		index: uint256,43	) -> Result<uint256, Self::Error>;44}4546#[derive(ToLog)]47pub enum ERC721Events {48	Transfer {49		#[indexed]50		from: address,51		#[indexed]52		to: address,53		#[indexed]54		token_id: uint256,55	},56	Approval {57		#[indexed]58		owner: address,59		#[indexed]60		approved: address,61		#[indexed]62		token_id: uint256,63	},64	#[allow(dead_code)]65	ApprovalForAll {66		#[indexed]67		owner: address,68		#[indexed]69		operator: address,70		approved: bool,71	},72}7374#[solidity_interface(is(ERC165), events(ERC721Events))]75pub trait ERC721 {76	type Error;7778	fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;79	fn owner_of(&self, token_id: uint256) -> Result<address, Self::Error>;8081	#[solidity(rename_selector = "safeTransferFrom")]82	fn safe_transfer_from_with_data(83		&mut self,84		from: address,85		to: address,86		token_id: uint256,87		data: bytes,88		value: value,89	) -> Result<void, Self::Error>;90	fn safe_transfer_from(91		&mut self,92		from: address,93		to: address,94		token_id: uint256,95		value: value,96	) -> Result<void, Self::Error>;9798	fn transfer_from(99		&mut self,100		caller: caller,101		from: address,102		to: address,103		token_id: uint256,104		value: value,105	) -> Result<void, Self::Error>;106	fn approve(107		&mut self,108		caller: caller,109		approved: address,110		token_id: uint256,111		value: value,112	) -> Result<void, Self::Error>;113	fn set_approval_for_all(114		&mut self,115		caller: caller,116		operator: address,117		approved: bool,118	) -> Result<void, Self::Error>;119120	fn get_approved(&self, token_id: uint256) -> Result<address, Self::Error>;121	fn is_approved_for_all(122		&self,123		owner: address,124		operator: address,125	) -> Result<address, Self::Error>;126}127128#[solidity_interface]129pub trait ERC721UniqueExtensions {130	type Error;131132	fn transfer(133		&mut self,134		caller: caller,135		to: address,136		token_id: uint256,137		value: value,138	) -> Result<void, Self::Error>;139}140141#[solidity_interface(is(142	ERC165,143	ERC721,144	ERC721Metadata,145	ERC721Enumerable,146	ERC721UniqueExtensions147))]148pub trait UniqueNFT {149	type Error;150}151152#[derive(ToLog)]153pub enum ERC20Events {154	Transfer {155		#[indexed]156		from: address,157		#[indexed]158		to: address,159		value: uint256,160	},161	Approval {162		#[indexed]163		owner: address,164		#[indexed]165		spender: address,166		value: uint256,167	},168}169170#[solidity_interface(inline_is(InlineNameSymbol, InlineTotalSupply), events(ERC20Events))]171pub trait ERC20 {172	type Error;173174	fn decimals(&self) -> Result<uint8, Self::Error>;175	fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;176	fn transfer(177		&mut self,178		caller: caller,179		to: address,180		value: uint256,181	) -> Result<bool, Self::Error>;182	fn transfer_from(183		&mut self,184		caller: caller,185		from: address,186		to: address,187		value: uint256,188	) -> Result<bool, Self::Error>;189	fn approve(190		&mut self,191		caller: caller,192		spender: address,193		value: uint256,194	) -> Result<bool, Self::Error>;195	fn allowance(&self, owner: address, spender: address) -> Result<uint256, Self::Error>;196}197198#[solidity_interface(is(ERC165, ERC20))]199pub trait UniqueFungible {200	type Error;201}
after · pallets/nft/src/eth/erc.rs
1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use core::convert::TryInto;4use alloc::format;5use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};6use frame_support::storage::StorageDoubleMap;7use pallet_evm::AddressMapping;8use super::account::CrossAccountId;9use sp_std::vec::Vec;1011#[solidity_interface(name = "ERC165")]12impl<T: Config> CollectionHandle<T> {13	fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {14		Ok(match self.mode {15			CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),16			CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),17			_ => false,18		})19	}20}2122#[solidity_interface(name = "InlineNameSymbol")]23impl<T: Config> CollectionHandle<T> {24	fn name(&self) -> Result<string> {25		Ok(decode_utf16(self.name.iter().copied())26			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))27			.collect::<string>())28	}2930	fn symbol(&self) -> Result<string> {31		Ok(string::from_utf8_lossy(&self.token_prefix).into())32	}33}3435#[solidity_interface(name = "InlineTotalSupply")]36impl<T: Config> CollectionHandle<T> {37	fn total_supply(&self) -> Result<uint256> {38		// TODO: we do not track total amount of all tokens39		Ok(0.into())40	}41}4243#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]44impl<T: Config> CollectionHandle<T> {45	fn token_uri(&self, token_id: uint256) -> Result<string> {46		// TODO: We should standartize url prefix, maybe via offchain schema?47		Ok(format!("unique.network/{}/{}", self.id, token_id))48	}49}5051#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]52impl<T: Config> CollectionHandle<T> {53	fn token_by_index(&self, index: uint256) -> Result<uint256> {54		Ok(index)55	}5657	fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {58		// TODO: Not implemetable59		Err("not implemented".into())60	}61}6263#[derive(ToLog)]64pub enum ERC721Events {65	Transfer {66		#[indexed]67		from: address,68		#[indexed]69		to: address,70		#[indexed]71		token_id: uint256,72	},73	Approval {74		#[indexed]75		owner: address,76		#[indexed]77		approved: address,78		#[indexed]79		token_id: uint256,80	},81	#[allow(dead_code)]82	ApprovalForAll {83		#[indexed]84		owner: address,85		#[indexed]86		operator: address,87		approved: bool,88	},89}9091#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]92impl<T: Config> CollectionHandle<T> {93	#[solidity(rename_selector = "balanceOf")]94	fn balance_of_nft(&self, owner: address) -> Result<uint256> {95		let owner = T::EvmAddressMapping::into_account_id(owner);96		let balance = <Balance<T>>::get(self.id, owner);97		Ok(balance.into())98	}99	fn owner_of(&self, token_id: uint256) -> Result<address> {100		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;101		let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;102		Ok(*token.owner.as_eth())103	}104	fn safe_transfer_from_with_data(105		&mut self,106		_from: address,107		_to: address,108		_token_id: uint256,109		_data: bytes,110		_value: value,111	) -> Result<void> {112		// TODO: Not implemetable113		Err("not implemented".into())114	}115	fn safe_transfer_from(116		&mut self,117		_from: address,118		_to: address,119		_token_id: uint256,120		_value: value,121	) -> Result<void> {122		// TODO: Not implemetable123		Err("not implemented".into())124	}125126	fn transfer_from(127		&mut self,128		caller: caller,129		from: address,130		to: address,131		token_id: uint256,132		_value: value,133	) -> Result<void> {134		let caller = T::CrossAccountId::from_eth(caller);135		let from = T::CrossAccountId::from_eth(from);136		let to = T::CrossAccountId::from_eth(to);137		let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;138139		<Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)140			.map_err(|_| "transferFrom error")?;141		Ok(())142	}143144	fn approve(145		&mut self,146		caller: caller,147		approved: address,148		token_id: uint256,149		_value: value,150	) -> Result<void> {151		let caller = T::CrossAccountId::from_eth(caller);152		let approved = T::CrossAccountId::from_eth(approved);153		let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;154155		<Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)156			.map_err(|_| "approve internal")?;157		Ok(())158	}159160	fn set_approval_for_all(161		&mut self,162		_caller: caller,163		_operator: address,164		_approved: bool,165	) -> Result<void> {166		// TODO: Not implemetable167		Err("not implemented".into())168	}169170	fn get_approved(&self, _token_id: uint256) -> Result<address> {171		// TODO: Not implemetable172		Err("not implemented".into())173	}174175	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {176		// TODO: Not implemetable177		Err("not implemented".into())178	}179}180181#[solidity_interface(name = "ERC721UniqueExtensions")]182impl<T: Config> CollectionHandle<T> {183	#[solidity(rename_selector = "transfer")]184	fn transfer_nft(185		&mut self,186		caller: caller,187		to: address,188		token_id: uint256,189		_value: value,190	) -> Result<void> {191		let caller = T::CrossAccountId::from_eth(caller);192		let to = T::CrossAccountId::from_eth(to);193		let token_id = token_id.try_into().map_err(|_| "amount overflow")?;194195		<Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)196			.map_err(|_| "transfer error")?;197		Ok(())198	}199}200201#[solidity_interface(202	name = "UniqueNFT",203	is(204		ERC165,205		ERC721,206		ERC721Metadata,207		ERC721Enumerable,208		ERC721UniqueExtensions209	)210)]211impl<T: Config> CollectionHandle<T> {}212213#[derive(ToLog)]214pub enum ERC20Events {215	Transfer {216		#[indexed]217		from: address,218		#[indexed]219		to: address,220		value: uint256,221	},222	Approval {223		#[indexed]224		owner: address,225		#[indexed]226		spender: address,227		value: uint256,228	},229}230231#[solidity_interface(232	name = "ERC20",233	inline_is(InlineNameSymbol, InlineTotalSupply),234	events(ERC20Events)235)]236impl<T: Config> CollectionHandle<T> {237	fn decimals(&self) -> Result<uint8> {238		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {239			*decimals240		} else {241			unreachable!()242		})243	}244	fn balance_of(&self, owner: address) -> Result<uint256> {245		let owner = T::EvmAddressMapping::into_account_id(owner);246		let balance = <Balance<T>>::get(self.id, owner);247		Ok(balance.into())248	}249	fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {250		let caller = T::CrossAccountId::from_eth(caller);251		let to = T::CrossAccountId::from_eth(to);252		let amount = amount.try_into().map_err(|_| "amount overflow")?;253254		<Module<T>>::transfer_internal(&caller, &to, self, 1, amount)255			.map_err(|_| "transfer error")?;256		Ok(true)257	}258	#[solidity(rename_selector = "transferFrom")]259	fn transfer_from_fungible(260		&mut self,261		caller: caller,262		from: address,263		to: address,264		amount: uint256,265	) -> Result<bool> {266		let caller = T::CrossAccountId::from_eth(caller);267		let from = T::CrossAccountId::from_eth(from);268		let to = T::CrossAccountId::from_eth(to);269		let amount = amount.try_into().map_err(|_| "amount overflow")?;270271		<Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)272			.map_err(|_| "transferFrom error")?;273		Ok(true)274	}275	#[solidity(rename_selector = "approve")]276	fn approve_fungible(277		&mut self,278		caller: caller,279		spender: address,280		amount: uint256,281	) -> Result<bool> {282		let caller = T::CrossAccountId::from_eth(caller);283		let spender = T::CrossAccountId::from_eth(spender);284		let amount = amount.try_into().map_err(|_| "amount overflow")?;285286		<Module<T>>::approve_internal(&caller, &spender, self, 1, amount)287			.map_err(|_| "approve internal")?;288		Ok(true)289	}290	fn allowance(&self, owner: address, spender: address) -> Result<uint256> {291		let owner = T::CrossAccountId::from_eth(owner);292		let spender = T::CrossAccountId::from_eth(spender);293294		Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())295	}296}297298#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]299impl<T: Config> CollectionHandle<T> {}
deletedpallets/nft/src/eth/erc_impl.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/erc_impl.rs
+++ /dev/null
@@ -1,283 +0,0 @@
-use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
-use evm_coder::{
-	abi::{AbiWriter, StringError},
-	types::*,
-};
-use core::convert::TryInto;
-use alloc::format;
-use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};
-use frame_support::storage::StorageDoubleMap;
-use pallet_evm::AddressMapping;
-use super::erc::*;
-use super::account::CrossAccountId;
-
-type Result<T> = core::result::Result<T, StringError>;
-
-impl<T: Config> InlineNameSymbol for CollectionHandle<T> {
-	type Error = StringError;
-
-	fn name(&self) -> Result<string> {
-		Ok(decode_utf16(self.name.iter().copied())
-			.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
-			.collect::<string>())
-	}
-
-	fn symbol(&self) -> Result<string> {
-		Ok(string::from_utf8_lossy(&self.token_prefix).into())
-	}
-}
-
-impl<T: Config> InlineTotalSupply for CollectionHandle<T> {
-	type Error = StringError;
-
-	fn total_supply(&self) -> Result<uint256> {
-		// TODO: we do not track total amount of all tokens
-		Ok(0.into())
-	}
-}
-
-impl<T: Config> ERC721Metadata for CollectionHandle<T> {
-	type Error = StringError;
-
-	fn token_uri(&self, token_id: uint256) -> Result<string> {
-		// TODO: We should standartize url prefix, maybe via offchain schema?
-		Ok(format!("unique.network/{}/{}", self.id, token_id))
-	}
-
-	fn call_inline_name_symbol(&mut self, c: Msg<InlineNameSymbolCall>) -> Result<AbiWriter> {
-		<Self as InlineNameSymbol>::call(self, c)
-	}
-}
-
-impl<T: Config> ERC721Enumerable for CollectionHandle<T> {
-	type Error = StringError;
-
-	fn token_by_index(&self, index: uint256) -> Result<uint256> {
-		Ok(index)
-	}
-
-	fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-
-	fn call_inline_total_supply(&mut self, c: Msg<InlineTotalSupplyCall>) -> Result<AbiWriter> {
-		<Self as InlineTotalSupply>::call(self, c)
-	}
-}
-
-impl<T: Config> ERC721 for CollectionHandle<T> {
-	type Error = StringError;
-
-	fn balance_of(&self, owner: address) -> Result<uint256> {
-		let owner = T::EvmAddressMapping::into_account_id(owner);
-		let balance = <Balance<T>>::get(self.id, owner);
-		Ok(balance.into())
-	}
-	fn owner_of(&self, token_id: uint256) -> Result<address> {
-		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
-		let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;
-		Ok(*token.owner.as_eth())
-	}
-	fn safe_transfer_from_with_data(
-		&mut self,
-		_from: address,
-		_to: address,
-		_token_id: uint256,
-		_data: bytes,
-		_value: value,
-	) -> Result<void> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-	fn safe_transfer_from(
-		&mut self,
-		_from: address,
-		_to: address,
-		_token_id: uint256,
-		_value: value,
-	) -> Result<void> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-
-	fn transfer_from(
-		&mut self,
-		caller: caller,
-		from: address,
-		to: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let from = T::CrossAccountId::from_eth(from);
-		let to = T::CrossAccountId::from_eth(to);
-		let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
-
-		<Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)
-			.map_err(|_| "transferFrom error")?;
-		Ok(())
-	}
-
-	fn approve(
-		&mut self,
-		caller: caller,
-		approved: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let approved = T::CrossAccountId::from_eth(approved);
-		let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
-
-		<Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)
-			.map_err(|_| "approve internal")?;
-		Ok(())
-	}
-
-	fn set_approval_for_all(
-		&mut self,
-		_caller: caller,
-		_operator: address,
-		_approved: bool,
-	) -> Result<void> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-
-	fn get_approved(&self, _token_id: uint256) -> Result<address> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-
-	fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
-		// TODO: Not implemetable
-		Err("not implemented".into())
-	}
-
-	fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {
-		let ERC165Call::SupportsInterface { interface_id } = c.call;
-		Ok(evm_coder::abi_encode!(bool(
-			&ERC721Call::supports_interface(interface_id)
-		)))
-	}
-}
-
-impl<T: Config> ERC721UniqueExtensions for CollectionHandle<T> {
-	type Error = StringError;
-	fn transfer(
-		&mut self,
-		caller: caller,
-		to: address,
-		token_id: uint256,
-		_value: value,
-	) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let to = T::CrossAccountId::from_eth(to);
-		let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
-
-		<Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)
-			.map_err(|_| "transfer error")?;
-		Ok(())
-	}
-}
-
-impl<T: Config> UniqueNFT for CollectionHandle<T> {
-	type Error = StringError;
-	fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {
-		let ERC165Call::SupportsInterface { interface_id } = c.call;
-		Ok(evm_coder::abi_encode!(bool(
-			&UniqueNFTCall::supports_interface(interface_id)
-		)))
-	}
-	fn call_erc721(&mut self, c: Msg<ERC721Call>) -> Result<AbiWriter> {
-		<Self as ERC721>::call(self, c)
-	}
-	fn call_erc721_metadata(&mut self, c: Msg<ERC721MetadataCall>) -> Result<AbiWriter> {
-		<Self as ERC721Metadata>::call(self, c)
-	}
-	fn call_erc721_enumerable(&mut self, c: Msg<ERC721EnumerableCall>) -> Result<AbiWriter> {
-		<Self as ERC721Enumerable>::call(self, c)
-	}
-	fn call_erc721_unique_extensions(
-		&mut self,
-		c: Msg<ERC721UniqueExtensionsCall>,
-	) -> Result<AbiWriter> {
-		<Self as ERC721UniqueExtensions>::call(self, c)
-	}
-}
-
-impl<T: Config> ERC20 for CollectionHandle<T> {
-	type Error = StringError;
-	fn decimals(&self) -> Result<uint8> {
-		Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
-			*decimals
-		} else {
-			unreachable!()
-		})
-	}
-	fn balance_of(&self, owner: address) -> Result<uint256> {
-		let owner = T::EvmAddressMapping::into_account_id(owner);
-		let balance = <Balance<T>>::get(self.id, owner);
-		Ok(balance.into())
-	}
-	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);
-		let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
-		<Module<T>>::transfer_internal(&caller, &to, self, 1, amount)
-			.map_err(|_| "transfer error")?;
-		Ok(true)
-	}
-	fn transfer_from(
-		&mut self,
-		caller: caller,
-		from: address,
-		to: address,
-		amount: uint256,
-	) -> Result<bool> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let from = T::CrossAccountId::from_eth(from);
-		let to = T::CrossAccountId::from_eth(to);
-		let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
-		<Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)
-			.map_err(|_| "transferFrom error")?;
-		Ok(true)
-	}
-	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);
-		let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
-		<Module<T>>::approve_internal(&caller, &spender, self, 1, amount)
-			.map_err(|_| "approve internal")?;
-		Ok(true)
-	}
-	fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
-		let owner = T::CrossAccountId::from_eth(owner);
-		let spender = T::CrossAccountId::from_eth(spender);
-
-		Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())
-	}
-	fn call_inline_name_symbol(&mut self, c: Msg<InlineNameSymbolCall>) -> Result<AbiWriter> {
-		<Self as InlineNameSymbol>::call(self, c)
-	}
-	fn call_inline_total_supply(&mut self, c: Msg<InlineTotalSupplyCall>) -> Result<AbiWriter> {
-		<Self as InlineTotalSupply>::call(self, c)
-	}
-}
-
-impl<T: Config> UniqueFungible for CollectionHandle<T> {
-	type Error = StringError;
-	fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {
-		let ERC165Call::SupportsInterface { interface_id } = c.call;
-		Ok(evm_coder::abi_encode!(bool(
-			&UniqueNFTCall::supports_interface(interface_id)
-		)))
-	}
-	fn call_erc20(&mut self, c: Msg<ERC20Call>) -> Result<AbiWriter> {
-		<Self as ERC20>::call(self, c)
-	}
-}
deletedpallets/nft/src/eth/log.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/log.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use sp_std::cell::RefCell;
-use sp_std::vec::Vec;
-
-use ethereum::Log;
-
-#[derive(Default)]
-pub struct LogRecorder(RefCell<Vec<Log>>);
-
-impl LogRecorder {
-	pub fn is_empty(&self) -> bool {
-		self.0.borrow().is_empty()
-	}
-	pub fn log(&self, log: Log) {
-		self.0.borrow_mut().push(log);
-	}
-	pub fn retrieve_logs(self) -> Vec<Log> {
-		self.0.into_inner()
-	}
-}
modifiedpallets/nft/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/mod.rs
+++ b/pallets/nft/src/eth/mod.rs
@@ -1,23 +1,15 @@
 pub mod account;
 pub mod erc;
-mod erc_impl;
-pub mod log;
 pub mod sponsoring;
 
-use evm_coder::abi::AbiWriter;
-use evm_coder::abi::StringError;
-use sp_std::prelude::*;
+use pallet_evm_coder_substrate::call_internal;
 use sp_std::borrow::ToOwned;
 use sp_std::vec::Vec;
-
-use pallet_evm::{PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};
+use pallet_evm::{PrecompileOutput};
 use sp_core::{H160, U256};
 use frame_support::storage::StorageMap;
-
 use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};
-
-use erc::{UniqueFungible, UniqueFungibleCall, UniqueNFT, UniqueNFTCall};
-use evm_coder::{types::*, abi::AbiReader};
+use erc::{UniqueFungibleCall, UniqueNFTCall};
 
 pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);
 
@@ -42,19 +34,17 @@
 	H160(out)
 }
 
+/*
 fn call_internal<T: Config>(
 	collection: &mut CollectionHandle<T>,
 	caller: caller,
 	method_id: u32,
 	mut input: AbiReader,
 	value: U256,
-) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {
+) -> Result<Option<AbiWriter>> {
 	match collection.mode.clone() {
 		CollectionMode::Fungible(_) => {
-			#[cfg(feature = "std")]
-			{
-				println!("Parse fungible call {:x}", method_id);
-			}
+			call_internal();
 			let call = match UniqueFungibleCall::parse(method_id, &mut input)? {
 				Some(v) => v,
 				None => {
@@ -65,10 +55,6 @@
 					return Ok(None);
 				}
 			};
-			#[cfg(feature = "std")]
-			{
-				dbg!(&call);
-			}
 			Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(
 				collection,
 				Msg {
@@ -92,11 +78,9 @@
 				},
 			)?))
 		}
-		_ => Err(StringError::from(
-			"erc calls only supported to fungible and nft collections for now",
-		)),
+		_ => Err("erc calls only supported to fungible and nft collections for now".into()),
 	}
-}
+}*/
 
 impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {
 	fn is_reserved(target: &H160) -> bool {
@@ -129,56 +113,15 @@
 	) -> Option<PrecompileOutput> {
 		let mut collection = map_eth_to_id(target)
 			.and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;
-		let (method_id, input) = AbiReader::new_call(input).unwrap();
-		let result = call_internal(&mut collection, *source, method_id, input, value);
-		let cost = gas_limit - collection.gas_left();
-		let logs = collection.logs.retrieve_logs();
-		match result {
-			Ok(Some(v)) => Some(PrecompileOutput {
-				exit_status: ExitReason::Succeed(ExitSucceed::Returned),
-				cost,
-				logs,
-				output: v.finish(),
-			}),
-			Ok(None) => None,
-			Err(e) => Some(PrecompileOutput {
-				exit_status: ExitReason::Revert(ExitRevert::Reverted),
-				cost: 0,
-				logs: Default::default(),
-				output: AbiWriter::from(e).finish(),
-			}),
-		}
-	}
-}
-
-// TODO: This function is slow, and output can be memoized
-pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {
-	// FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728
-	#[cfg(feature = "std")]
-	{
-		let contract = collection_id_to_address(collection_id);
-		let signed = ethereum_tx_sign::RawTransaction {
-			nonce: 0.into(),
-			to: Some(contract.0.into()),
-			value: 0.into(),
-			gas_price: 0.into(),
-			gas: 0.into(),
-			// zero selector, this transaction always have same sender, so all data should be acquired from logs
-			data: Vec::from([0, 0, 0, 0]),
-		}
-		.sign(
-			// TODO: move to pallet config
-			// 0xF70631E55faff9f3FD3681545aa6c724226a3853
-			// 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a
-			&hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a")
-				.into(),
-			&chain_id,
-		);
-		rlp::decode::<ethereum::Transaction>(&signed)
-			.expect("transaction is just created, it can't be broken")
-	}
-	#[cfg(not(feature = "std"))]
-	{
-		panic!("transaction generation not yet supported by wasm runtime while generating transaction for collection_id {}, chain_id {}", collection_id, chain_id)
+		let result = match collection.mode {
+			CollectionMode::NFT => {
+				call_internal::<UniqueNFTCall, _>(*source, &mut collection, value, input)
+			}
+			CollectionMode::Fungible(_) => {
+				call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)
+			}
+			_ => return None,
+		};
+		collection.recorder.evm_to_precompile_output(result)
 	}
 }
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -36,14 +36,12 @@
 use sp_std::vec;
 use sp_runtime::sp_std::prelude::Vec;
 use core::ops::{Deref, DerefMut};
-use core::cell::RefCell;
 use nft_data_structs::{
 	MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,
 	AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,
 	CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,
 	FungibleItemType, ReFungibleItemType,
 };
-use pallet_ethereum::EthereumTransactionSender;
 
 #[cfg(test)]
 mod mock;
@@ -55,6 +53,7 @@
 mod eth;
 mod sponsorship;
 pub use sponsorship::NftSponsorshipHandler;
+pub use eth::sponsoring::NftEthSponsorshipHandler;
 
 pub use eth::NftErcSupport;
 pub use eth::account::*;
@@ -178,42 +177,39 @@
 	}
 }
 
+#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
 pub struct CollectionHandle<T: Config> {
 	pub id: CollectionId,
 	collection: Collection<T>,
-	logs: eth::log::LogRecorder,
-	evm_address: H160,
-	gas_limit: RefCell<u64>,
+	recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
 }
 impl<T: Config> CollectionHandle<T> {
 	pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
 		<CollectionById<T>>::get(id).map(|collection| Self {
 			id,
 			collection,
-			logs: eth::log::LogRecorder::default(),
-			evm_address: eth::collection_id_to_address(id),
-			gas_limit: RefCell::new(gas_limit),
+			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(
+				eth::collection_id_to_address(id),
+				gas_limit,
+			),
 		})
 	}
 	pub fn get(id: CollectionId) -> Option<Self> {
 		Self::get_with_gas_limit(id, u64::MAX)
 	}
-	pub fn gas_left(&self) -> u64 {
-		*self.gas_limit.borrow()
+	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
+		self.recorder.log_sub(log)
 	}
-	pub fn consume_gas(&self, gas: u64) -> DispatchResult {
-		let mut gas_limit = self.gas_limit.borrow_mut();
-		if *gas_limit < gas {
-			fail!(Error::<T>::OutOfGas);
-		}
-		*gas_limit -= gas;
-		Ok(())
+	fn consume_gas(&self, gas: u64) -> DispatchResult {
+		self.recorder.consume_gas_sub(gas)
 	}
-	pub fn log(&self, log: impl evm_coder::ToLog) {
-		self.logs.log(log.to_log(self.evm_address))
+	pub fn submit_logs(self) -> DispatchResult {
+		self.recorder.submit_logs()
 	}
-	pub fn into_inner(self) -> Collection<T> {
-		self.collection
+	pub fn save(self) -> DispatchResult {
+		self.recorder.submit_logs()?;
+		<CollectionById<T>>::insert(self.id, self.collection);
+		Ok(())
 	}
 }
 impl<T: Config> Deref for CollectionHandle<T> {
@@ -230,7 +226,7 @@
 	}
 }
 
-pub trait Config: system::Config + Sized {
+pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {
 	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
 
 	/// Weight information for extrinsics in this pallet.
@@ -245,9 +241,6 @@
 		<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
 	>;
 	type TreasuryAccountId: Get<Self::AccountId>;
-
-	type EthereumChainId: Get<u64>;
-	type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;
 }
 
 // # Used definitions
@@ -564,23 +557,23 @@
 				fail!(Error::<T>::NoPermission);
 			}
 
-			<AddressTokens<T>>::remove_prefix(collection_id);
-			<Allowances<T>>::remove_prefix(collection_id);
-			<Balance<T>>::remove_prefix(collection_id);
+			<AddressTokens<T>>::remove_prefix(collection_id, None);
+			<Allowances<T>>::remove_prefix(collection_id, None);
+			<Balance<T>>::remove_prefix(collection_id, None);
 			<ItemListIndex>::remove(collection_id);
 			<AdminList<T>>::remove(collection_id);
 			<CollectionById<T>>::remove(collection_id);
-			<WhiteList<T>>::remove_prefix(collection_id);
+			<WhiteList<T>>::remove_prefix(collection_id, None);
 
-			<NftItemList<T>>::remove_prefix(collection_id);
-			<FungibleItemList<T>>::remove_prefix(collection_id);
-			<ReFungibleItemList<T>>::remove_prefix(collection_id);
+			<NftItemList<T>>::remove_prefix(collection_id, None);
+			<FungibleItemList<T>>::remove_prefix(collection_id, None);
+			<ReFungibleItemList<T>>::remove_prefix(collection_id, None);
 
-			<NftTransferBasket<T>>::remove_prefix(collection_id);
-			<FungibleTransferBasket<T>>::remove_prefix(collection_id);
-			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id);
+			<NftTransferBasket<T>>::remove_prefix(collection_id, None);
+			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
+			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);
 
-			<VariableMetaDataBasket<T>>::remove_prefix(collection_id);
+			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);
 
 			DestroyedCollectionCount::put(DestroyedCollectionCount::get()
 				.checked_add(1)
@@ -667,9 +660,7 @@
 			let mut target_collection = Self::get_collection(collection_id)?;
 			Self::check_owner_permissions(&target_collection, &sender)?;
 			target_collection.access = mode;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Allows Anyone to create tokens if:
@@ -694,9 +685,7 @@
 			let mut target_collection = Self::get_collection(collection_id)?;
 			Self::check_owner_permissions(&target_collection, &sender)?;
 			target_collection.mint_mode = mint_permission;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Change the owner of the collection.
@@ -718,9 +707,7 @@
 			let mut target_collection = Self::get_collection(collection_id)?;
 			Self::check_owner_permissions(&target_collection, &sender)?;
 			target_collection.owner = new_owner;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Adds an admin of the Collection.
@@ -800,9 +787,7 @@
 			Self::check_owner_permissions(&target_collection, &sender)?;
 
 			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// # Permissions
@@ -824,9 +809,7 @@
 			);
 
 			target_collection.sponsorship = SponsorshipState::Confirmed(sender);
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Switch back to pay-per-own-transaction model.
@@ -847,9 +830,7 @@
 			Self::check_owner_permissions(&target_collection, &sender)?;
 
 			target_collection.sponsorship = SponsorshipState::Disabled;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.
@@ -884,8 +865,7 @@
 
 			Self::create_item_internal(&sender, &collection, &owner, data)?;
 
-			Self::submit_logs(collection)?;
-			Ok(())
+			collection.submit_logs()
 		}
 
 		/// This method creates multiple items in a collection created with CreateCollection method.
@@ -918,8 +898,7 @@
 
 			Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;
 
-			Self::submit_logs(collection)?;
-			Ok(())
+			collection.submit_logs()
 		}
 
 		/// Destroys a concrete instance of NFT.
@@ -944,8 +923,7 @@
 
 			Self::burn_item_internal(&sender, &target_collection, item_id, value)?;
 
-			Self::submit_logs(target_collection)?;
-			Ok(())
+			target_collection.submit_logs()
 		}
 
 		/// Change ownership of the token.
@@ -979,8 +957,7 @@
 
 			Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;
 
-			Self::submit_logs(collection)?;
-			Ok(())
+			collection.submit_logs()
 		}
 
 		/// Set, change, or remove approved address to transfer the ownership of the NFT.
@@ -1006,8 +983,7 @@
 
 			Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;
 
-			Self::submit_logs(collection)?;
-			Ok(())
+			collection.submit_logs()
 		}
 
 		/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
@@ -1037,8 +1013,7 @@
 
 			Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;
 
-			Self::submit_logs(collection)?;
-			Ok(())
+			collection.submit_logs()
 		}
 		// #[weight = 0]
 		//     // let no_perm_mes = "You do not have permissions to modify this collection";
@@ -1107,9 +1082,7 @@
 			let mut target_collection = Self::get_collection(collection_id)?;
 			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;
 			target_collection.schema_version = version;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Set off-chain data schema.
@@ -1139,9 +1112,7 @@
 			ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");
 
 			target_collection.offchain_schema = schema;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Set const on-chain data schema.
@@ -1171,9 +1142,7 @@
 			ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");
 
 			target_collection.const_on_chain_schema = schema;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		/// Set variable on-chain data schema.
@@ -1203,9 +1172,7 @@
 			ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");
 
 			target_collection.variable_on_chain_schema = schema;
-			Self::save_collection(target_collection);
-
-			Ok(())
+			target_collection.save()
 		}
 
 		// Sudo permissions function
@@ -1253,9 +1220,8 @@
 			);
 
 			target_collection.limits = new_limits;
-			Self::save_collection(target_collection);
 
-			Ok(())
+			target_collection.save()
 		}
 	}
 }
@@ -1376,7 +1342,7 @@
 				owner: *sender.as_eth(),
 				approved: *spender.as_eth(),
 				token_id: item_id.into(),
-			});
+			})?;
 		}
 
 		if matches!(collection.mode, CollectionMode::Fungible(_)) {
@@ -1385,7 +1351,7 @@
 				owner: *sender.as_eth(),
 				spender: *spender.as_eth(),
 				value: allowance.into(),
-			});
+			})?;
 		}
 
 		Self::deposit_event(RawEvent::Approved(
@@ -1461,7 +1427,7 @@
 				owner: *from.as_eth(),
 				spender: *sender.as_eth(),
 				value: allowance.into(),
-			});
+			})?;
 		}
 
 		Ok(())
@@ -1789,7 +1755,7 @@
 			from: H160::default(),
 			to: *item_owner.as_eth(),
 			token_id: current_index.into(),
-		});
+		})?;
 		Self::deposit_event(RawEvent::ItemCreated(
 			collection_id,
 			current_index,
@@ -1886,7 +1852,7 @@
 			from: *owner.as_eth(),
 			to: H160::default(),
 			value: value.into(),
-		});
+		})?;
 		Ok(())
 	}
 
@@ -1894,22 +1860,8 @@
 		collection_id: CollectionId,
 	) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {
 		Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)
-	}
-
-	fn save_collection(collection: CollectionHandle<T>) {
-		<CollectionById<T>>::insert(collection.id, collection.into_inner());
 	}
 
-	pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {
-		if collection.logs.is_empty() {
-			return Ok(());
-		}
-		T::EthereumTransactionSender::submit_logs_transaction(
-			eth::generate_transaction(collection.id, T::EthereumChainId::get()),
-			collection.logs.retrieve_logs(),
-		)
-	}
-
 	fn check_owner_permissions(
 		target_collection: &CollectionHandle<T>,
 		subject: &T::AccountId,
@@ -2037,7 +1989,7 @@
 			from: *owner.as_eth(),
 			to: *recipient.as_eth(),
 			value: value.into(),
-		});
+		})?;
 		Self::deposit_event(RawEvent::Transfer(
 			collection.id,
 			1,
@@ -2173,7 +2125,7 @@
 			from: *sender.as_eth(),
 			to: *new_owner.as_eth(),
 			token_id: item_id.into(),
-		});
+		})?;
 		Self::deposit_event(RawEvent::Transfer(
 			collection.id,
 			item_id,
modifiedpallets/nft/src/mock.rsdiffbeforeafterboth
--- a/pallets/nft/src/mock.rs
+++ b/pallets/nft/src/mock.rs
@@ -193,7 +193,6 @@
 	type EvmAddressMapping = TestEvmAddressMapping;
 	type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;
 	type CrossAccountId = TestCrossAccountId;
-	type EthereumChainId = EthereumChainId;
 	type EthereumTransactionSender = TestEtheremTransactionSender;
 }