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}
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;
 }