git.delta.rocks / unique-network / refs/commits / 57b97ca5400c

difftreelog

refactor split common pallet

Yaroslav Bolyukin2021-10-12parent: #5db875a.patch.diff
in: master

16 files changed

addedpallets/common/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "pallet-common"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[dependencies]
+frame-support = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+frame-system = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-runtime = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-std = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-core = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+nft-data-structs = { default-features = false, path = '../../primitives/nft' }
+pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+
+pallet-evm = { default-features = false, version = "6.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.10" }
+serde = { version = "1.0.130", default-features = false }
+
+[features]
+default = ["std"]
+std = [
+    "frame-support/std",
+    "frame-system/std",
+    "sp-runtime/std",
+    "sp-std/std",
+    "nft-data-structs/std",
+    "pallet-evm/std",
+]
+runtime-benchmarks = []
addedpallets/common/src/account.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/account.rs
@@ -0,0 +1,142 @@
+use crate::Config;
+use codec::{Encode, EncodeLike, Decode};
+use sp_core::H160;
+use sp_core::crypto::AccountId32;
+use core::cmp::Ordering;
+use serde::{Serialize, Deserialize};
+use pallet_evm::AddressMapping;
+use sp_std::vec::Vec;
+use sp_std::clone::Clone;
+
+pub trait CrossAccountId<AccountId>:
+	Encode + EncodeLike + Decode + Clone + PartialEq + Ord + core::fmt::Debug + Default
+// +
+// Serialize + Deserialize<'static>
+{
+	fn as_sub(&self) -> &AccountId;
+	fn as_eth(&self) -> &H160;
+
+	fn from_sub(account: AccountId) -> Self;
+	fn from_eth(account: H160) -> Self;
+}
+
+#[derive(Eq, Serialize, Deserialize)]
+pub struct BasicCrossAccountId<T: Config> {
+	/// If true - then ethereum is canonical encoding
+	from_ethereum: bool,
+	substrate: T::AccountId,
+	ethereum: H160,
+}
+
+impl<T: Config> Default for BasicCrossAccountId<T> {
+	fn default() -> Self {
+		Self::from_sub(T::AccountId::default())
+	}
+}
+
+impl<T: Config> core::fmt::Debug for BasicCrossAccountId<T> {
+	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+		if self.from_ethereum {
+			fmt.debug_tuple("CrossAccountId::Ethereum")
+				.field(&self.ethereum)
+				.finish()
+		} else {
+			fmt.debug_tuple("CrossAccountId::Substrate")
+				.field(&self.substrate)
+				.finish()
+		}
+	}
+}
+
+impl<T: Config> PartialOrd for BasicCrossAccountId<T> {
+	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+		Some(self.substrate.cmp(&other.substrate))
+	}
+}
+
+impl<T: Config> Ord for BasicCrossAccountId<T> {
+	fn cmp(&self, other: &Self) -> Ordering {
+		self.partial_cmp(other)
+			.expect("substrate account is total ordered")
+	}
+}
+
+impl<T: Config> PartialEq for BasicCrossAccountId<T> {
+	fn eq(&self, other: &Self) -> bool {
+		if self.from_ethereum == other.from_ethereum {
+			self.substrate == other.substrate && self.ethereum == other.ethereum
+		} else if self.from_ethereum {
+			// ethereum is canonical encoding, but we need to compare derived address
+			self.substrate == other.substrate
+		} else {
+			self.ethereum == other.ethereum
+		}
+	}
+}
+impl<T: Config> Clone for BasicCrossAccountId<T> {
+	fn clone(&self) -> Self {
+		Self {
+			from_ethereum: self.from_ethereum,
+			substrate: self.substrate.clone(),
+			ethereum: self.ethereum,
+		}
+	}
+}
+impl<T: Config> Encode for BasicCrossAccountId<T> {
+	fn encode(&self) -> Vec<u8> {
+		let as_result = if !self.from_ethereum {
+			Ok(self.substrate.clone())
+		} else {
+			Err(self.ethereum)
+		};
+		as_result.encode()
+	}
+}
+impl<T: Config> EncodeLike for BasicCrossAccountId<T> {}
+impl<T: Config> Decode for BasicCrossAccountId<T> {
+	fn decode<I>(input: &mut I) -> Result<Self, codec::Error>
+	where
+		I: codec::Input,
+	{
+		Ok(match <Result<T::AccountId, H160>>::decode(input)? {
+			Ok(s) => Self::from_sub(s),
+			Err(e) => Self::from_eth(e),
+		})
+	}
+}
+impl<T: Config> CrossAccountId<T::AccountId> for BasicCrossAccountId<T> {
+	fn as_sub(&self) -> &T::AccountId {
+		&self.substrate
+	}
+	fn as_eth(&self) -> &H160 {
+		&self.ethereum
+	}
+	fn from_sub(substrate: T::AccountId) -> Self {
+		Self {
+			ethereum: T::EvmBackwardsAddressMapping::from_account_id(substrate.clone()),
+			substrate,
+			from_ethereum: false,
+		}
+	}
+	fn from_eth(ethereum: H160) -> Self {
+		Self {
+			ethereum,
+			substrate: T::EvmAddressMapping::into_account_id(ethereum),
+			from_ethereum: true,
+		}
+	}
+}
+
+pub trait EvmBackwardsAddressMapping<AccountId> {
+	fn from_account_id(account_id: AccountId) -> H160;
+}
+
+/// Should have same mapping as EnsureAddressTruncated
+pub struct MapBackwardsAddressTruncated;
+impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
+	fn from_account_id(account_id: AccountId32) -> H160 {
+		let mut out = [0; 20];
+		out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
+		H160(out)
+	}
+}
addedpallets/common/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/benchmarking.rs
@@ -0,0 +1 @@
+#![cfg(feature = "runtime-benchmarking")]
addedpallets/common/src/erc.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/erc.rs
@@ -0,0 +1,10 @@
+pub use pallet_evm::PrecompileOutput;
+use sp_core::{H160, U256};
+
+/// Does not always represent a full collection, for RFT it is either
+/// collection (Implementing ERC721), or specific collection token (Implementing ERC20)
+pub trait CommonEvmHandler {
+	const CODE: &'static [u8];
+
+	fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput>;
+}
addedpallets/common/src/eth.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/eth.rs
@@ -0,0 +1,50 @@
+use nft_data_structs::{CollectionId, TokenId};
+use sp_core::H160;
+
+// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1
+// TODO: Unhardcode prefix
+const ETH_ACCOUNT_PREFIX: [u8; 16] = [
+	0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,
+];
+
+// 0xf8238ccfff8ed887463fd5e00000000100000002  - collection 1, token 2
+// TODO: Unhardcode prefix
+const ETH_ACCOUNT_TOKEN_PREFIX: [u8; 12] = [
+	0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0,
+];
+
+pub fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {
+	if eth[0..16] != ETH_ACCOUNT_PREFIX {
+		return None;
+	}
+	let mut id_bytes = [0; 4];
+	id_bytes.copy_from_slice(&eth[16..20]);
+	Some(CollectionId(u32::from_be_bytes(id_bytes)))
+}
+pub fn collection_id_to_address(id: CollectionId) -> H160 {
+	let mut out = [0; 20];
+	out[0..16].copy_from_slice(&ETH_ACCOUNT_PREFIX);
+	out[16..20].copy_from_slice(&u32::to_be_bytes(id.0));
+	H160(out)
+}
+
+pub fn map_eth_to_token_id(eth: &H160) -> Option<(CollectionId, TokenId)> {
+	if eth[0..12] != ETH_ACCOUNT_TOKEN_PREFIX {
+		return None;
+	}
+	let mut id_bytes = [0; 4];
+	let mut token_id_bytes = [0; 4];
+	id_bytes.copy_from_slice(&eth[12..16]);
+	token_id_bytes.copy_from_slice(&eth[16..20]);
+	Some((
+		CollectionId(u32::from_be_bytes(id_bytes)),
+		TokenId(u32::from_be_bytes(token_id_bytes)),
+	))
+}
+pub fn collection_token_id_to_address(id: CollectionId, token: TokenId) -> H160 {
+	let mut out = [0; 20];
+	out[0..12].copy_from_slice(&ETH_ACCOUNT_TOKEN_PREFIX);
+	out[12..16].copy_from_slice(&u32::to_be_bytes(id.0));
+	out[16..20].copy_from_slice(&u32::to_be_bytes(token.0));
+	H160(out)
+}
addedpallets/common/src/lib.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/common/src/lib.rs
@@ -0,0 +1,543 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use core::ops::{Deref, DerefMut};
+use sp_std::vec::Vec;
+use account::CrossAccountId;
+use frame_support::{
+	dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},
+	ensure, fail,
+	traits::{Imbalance, Get, Currency},
+};
+use nft_data_structs::{
+	COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
+	MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+	MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, WithdrawReasons,
+};
+pub use pallet::*;
+use sp_core::H160;
+use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
+pub mod account;
+pub mod benchmarking;
+pub mod erc;
+pub mod eth;
+
+#[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>,
+	pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
+}
+impl<T: Config> CollectionHandle<T> {
+	pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
+		<CollectionById<T>>::get(id).map(|collection| Self {
+			id,
+			collection,
+			recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(
+				eth::collection_id_to_address(id),
+				gas_limit,
+			),
+		})
+	}
+	pub fn new(id: CollectionId) -> Option<Self> {
+		Self::new_with_gas_limit(id, u64::MAX)
+	}
+	pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {
+		Ok(Self::new(id).ok_or_else(|| <Error<T>>::CollectionNotFound)?)
+	}
+	pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
+		self.recorder.log_sub(log)
+	}
+	pub fn log_infallible(&self, log: impl evm_coder::ToLog) {
+		self.recorder.log_infallible(log)
+	}
+	#[allow(dead_code)]
+	fn consume_gas(&self, gas: u64) -> DispatchResult {
+		self.recorder.consume_gas_sub(gas)
+	}
+	pub fn consume_sload(&self) -> DispatchResult {
+		self.recorder.consume_sload_sub()
+	}
+	pub fn consume_sstores(&self, amount: usize) -> DispatchResult {
+		self.recorder.consume_sstores_sub(amount)
+	}
+	pub fn consume_sstore(&self) -> DispatchResult {
+		self.recorder.consume_sstore_sub()
+	}
+	pub fn consume_log(&self, topics: usize, data: usize) -> DispatchResult {
+		self.recorder.consume_log_sub(topics, data)
+	}
+	pub fn submit_logs(self) -> DispatchResult {
+		self.recorder.submit_logs()
+	}
+	pub fn save(self) -> DispatchResult {
+		self.recorder.submit_logs()?;
+		<CollectionById<T>>::insert(self.id, self.collection);
+		Ok(())
+	}
+}
+impl<T: Config> Deref for CollectionHandle<T> {
+	type Target = Collection<T>;
+
+	fn deref(&self) -> &Self::Target {
+		&self.collection
+	}
+}
+
+impl<T: Config> DerefMut for CollectionHandle<T> {
+	fn deref_mut(&mut self) -> &mut Self::Target {
+		&mut self.collection
+	}
+}
+
+impl<T: Config> CollectionHandle<T> {
+	pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {
+		ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);
+		Ok(())
+	}
+	pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> Result<bool, DispatchError> {
+		self.consume_sload()?;
+
+		Ok(*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject.as_sub())))
+	}
+	pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {
+		ensure!(self.is_owner_or_admin(subject)?, <Error<T>>::NoPermission);
+		Ok(())
+	}
+	pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
+		Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+	}
+	pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
+		Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+	}
+	pub fn check_whitelist(&self, user: &T::CrossAccountId) -> DispatchResult {
+		self.consume_sload()?;
+
+		ensure!(
+			<WhiteList<T>>::get((self.id, user.as_sub())),
+			<Error<T>>::AddressNotInWhiteList
+		);
+		Ok(())
+	}
+
+	pub fn check_can_update_meta(
+		&self,
+		subject: &T::CrossAccountId,
+		item_owner: &T::CrossAccountId,
+	) -> DispatchResult {
+		match self.meta_update_permission {
+			MetaUpdatePermission::ItemOwner => {
+				ensure!(subject == item_owner, <Error<T>>::NoPermission);
+				Ok(())
+			}
+			MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),
+			MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),
+		}
+	}
+}
+
+#[frame_support::pallet]
+pub mod pallet {
+	use super::*;
+	use frame_support::{pallet_prelude::*};
+	use frame_support::{Blake2_128Concat, storage::Key};
+	use account::{EvmBackwardsAddressMapping, CrossAccountId};
+	use frame_support::traits::Currency;
+	use nft_data_structs::TokenId;
+
+	#[pallet::config]
+	pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config {
+		type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
+
+		type CrossAccountId: CrossAccountId<Self::AccountId>;
+
+		type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
+		type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
+
+		type Currency: Currency<Self::AccountId>;
+		type CollectionCreationPrice: Get<
+			<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
+		>;
+		type TreasuryAccountId: Get<Self::AccountId>;
+	}
+
+	#[pallet::pallet]
+	#[pallet::generate_store(pub(super) trait Store)]
+	pub struct Pallet<T>(_);
+
+	#[pallet::event]
+	#[pallet::generate_deposit(pub fn deposit_event)]
+	pub enum Event<T: Config> {
+		/// New collection was created
+		///
+		/// # Arguments
+		///
+		/// * collection_id: Globally unique identifier of newly created collection.
+		///
+		/// * mode: [CollectionMode] converted into u8.
+		///
+		/// * account_id: Collection owner.
+		CollectionCreated(CollectionId, u8, T::AccountId),
+
+		/// New item was created.
+		///
+		/// # Arguments
+		///
+		/// * collection_id: Id of the collection where item was created.
+		///
+		/// * item_id: Id of an item. Unique within the collection.
+		///
+		/// * recipient: Owner of newly created item
+		///
+		/// * amount: Always 1 for NFT
+		ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),
+
+		/// Collection item was burned.
+		///
+		/// # Arguments
+		///
+		/// * collection_id.
+		///
+		/// * item_id: Identifier of burned NFT.
+		///
+		/// * owner: which user has destroyed its tokens
+		///
+		/// * amount: Always 1 for NFT
+		ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
+
+		/// Item was transferred
+		///
+		/// * collection_id: Id of collection to which item is belong
+		///
+		/// * item_id: Id of an item
+		///
+		/// * sender: Original owner of item
+		///
+		/// * recipient: New owner of item
+		///
+		/// * amount: Always 1 for NFT
+		Transfer(
+			CollectionId,
+			TokenId,
+			T::CrossAccountId,
+			T::CrossAccountId,
+			u128,
+		),
+
+		/// * collection_id
+		///
+		/// * item_id
+		///
+		/// * sender
+		///
+		/// * spender
+		///
+		/// * amount
+		Approved(
+			CollectionId,
+			TokenId,
+			T::CrossAccountId,
+			T::CrossAccountId,
+			u128,
+		),
+	}
+
+	#[pallet::error]
+	pub enum Error<T> {
+		/// This collection does not exist.
+		CollectionNotFound,
+		/// Sender parameter and item owner must be equal.
+		MustBeTokenOwner,
+		/// No permission to perform action
+		NoPermission,
+		/// Collection is not in mint mode.
+		PublicMintingNotAllowed,
+		/// Address is not in white list.
+		AddressNotInWhiteList,
+
+		/// Collection name can not be longer than 63 char.
+		CollectionNameLimitExceeded,
+		/// Collection description can not be longer than 255 char.
+		CollectionDescriptionLimitExceeded,
+		/// Token prefix can not be longer than 15 char.
+		CollectionTokenPrefixLimitExceeded,
+		/// Total collections bound exceeded.
+		TotalCollectionsLimitExceeded,
+		/// variable_data exceeded data limit.
+		TokenVariableDataLimitExceeded,
+
+		/// Collection settings not allowing items transferring
+		TransferNotAllowed,
+		/// Account token limit exceeded per collection
+		AccountTokenLimitExceeded,
+		/// Collection token limit exceeded
+		CollectionTokenLimitExceeded,
+		/// Metadata flag frozen
+		MetadataFlagFrozen,
+
+		/// Item not exists.
+		TokenNotFound,
+		/// Item balance not enough.
+		TokenValueTooLow,
+		/// Requested value more than approved.
+		TokenValueNotEnough,
+		/// Tried to approve more than owned
+		CantApproveMoreThanOwned,
+
+		/// Can't transfer tokens to ethereum zero address
+		AddressIsZero,
+		/// Target collection doesn't supports this operation
+		UnsupportedOperation,
+	}
+
+	#[pallet::storage]
+	pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+	#[pallet::storage]
+	pub type DestroyedCollectionCount<T> =
+		StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+
+	/// Collection info
+	#[pallet::storage]
+	pub type CollectionById<T> = StorageMap<
+		Hasher = Blake2_128Concat,
+		Key = CollectionId,
+		Value = Collection<T>,
+		QueryKind = OptionQuery,
+	>;
+
+	/// List of collection admins
+	#[pallet::storage]
+	pub type IsAdmin<T: Config> = StorageNMap<
+		Key = (
+			Key<Blake2_128Concat, CollectionId>,
+			Key<Blake2_128Concat, T::AccountId>,
+		),
+		Value = bool,
+		QueryKind = ValueQuery,
+	>;
+
+	/// Whitelisted collection users
+	#[pallet::storage]
+	pub type WhiteList<T: Config> = StorageNMap<
+		Key = (
+			Key<Blake2_128Concat, CollectionId>,
+			Key<Blake2_128Concat, T::AccountId>,
+		),
+		Value = bool,
+		QueryKind = ValueQuery,
+	>;
+}
+
+impl<T: Config> Pallet<T> {
+	/// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens
+	pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {
+		ensure!(
+			&T::CrossAccountId::from_eth(H160([0; 20])) != receiver,
+			<Error<T>>::AddressIsZero
+		);
+		Ok(())
+	}
+}
+
+impl<T: Config> Pallet<T> {
+	pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
+		{
+			ensure!(
+				data.name.len() <= MAX_COLLECTION_NAME_LENGTH,
+				Error::<T>::CollectionNameLimitExceeded
+			);
+			ensure!(
+				data.description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH,
+				Error::<T>::CollectionDescriptionLimitExceeded
+			);
+			ensure!(
+				data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH,
+				Error::<T>::CollectionTokenPrefixLimitExceeded
+			);
+		}
+
+		let created_count = <CreatedCollectionCount<T>>::get()
+			.0
+			.checked_add(1)
+			.ok_or(ArithmeticError::Overflow)?;
+		let destroyed_count = <DestroyedCollectionCount<T>>::get().0;
+		let id = CollectionId(created_count);
+
+		// bound Total number of collections
+		ensure!(
+			created_count - destroyed_count < COLLECTION_NUMBER_LIMIT,
+			<Error<T>>::TotalCollectionsLimitExceeded
+		);
+
+		// =========
+
+		// Take a (non-refundable) deposit of collection creation
+		{
+			let mut imbalance =
+				<<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();
+			imbalance.subsume(
+				<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(
+					&T::TreasuryAccountId::get(),
+					T::CollectionCreationPrice::get(),
+				),
+			);
+			<T as Config>::Currency::settle(
+				&data.owner,
+				imbalance,
+				WithdrawReasons::TRANSFER,
+				ExistenceRequirement::KeepAlive,
+			)
+			.map_err(|_| Error::<T>::NoPermission)?;
+		}
+
+		<CreatedCollectionCount<T>>::put(created_count);
+		<Pallet<T>>::deposit_event(Event::CollectionCreated(
+			id,
+			data.mode.id(),
+			data.owner.clone(),
+		));
+		<CollectionById<T>>::insert(id, data);
+		Ok(id)
+	}
+	pub fn destroy_collection(
+		collection: CollectionHandle<T>,
+		sender: &T::CrossAccountId,
+	) -> DispatchResult {
+		if !collection.limits.owner_can_destroy {
+			fail!(Error::<T>::NoPermission);
+		}
+		collection.check_is_owner(&sender)?;
+
+		let destroyed_collections = <DestroyedCollectionCount<T>>::get()
+			.0
+			.checked_add(1)
+			.ok_or(ArithmeticError::Overflow)?;
+
+		// =========
+
+		<DestroyedCollectionCount<T>>::put(destroyed_collections);
+		<CollectionById<T>>::remove(collection.id);
+		<IsAdmin<T>>::remove_prefix((collection.id,), None);
+		<WhiteList<T>>::remove_prefix((collection.id,), None);
+		Ok(())
+	}
+
+	pub fn toggle_whitelist(
+		collection: &CollectionHandle<T>,
+		sender: &T::CrossAccountId,
+		user: &T::CrossAccountId,
+		allowed: bool,
+	) -> DispatchResult {
+		collection.check_is_owner_or_admin(&sender)?;
+
+		// =========
+
+		if allowed {
+			<WhiteList<T>>::insert((collection.id, user.as_sub()), true);
+		} else {
+			<WhiteList<T>>::remove((collection.id, user.as_sub()));
+		}
+
+		Ok(())
+	}
+}
+
+#[macro_export]
+macro_rules! unsupported {
+	() => {
+		Err(<Error<T>>::UnsupportedOperation.into())
+	};
+}
+
+/// Worst cases
+pub trait CommonWeightInfo {
+	fn create_item() -> Weight;
+	fn create_multiple_items(amount: u32) -> Weight;
+	fn burn_item() -> Weight;
+	fn transfer() -> Weight;
+	fn approve() -> Weight;
+	fn transfer_from() -> Weight;
+	fn set_variable_metadata(bytes: u32) -> Weight;
+}
+
+pub trait CommonCollectionOperations<T: Config> {
+	fn create_item(
+		&self,
+		sender: T::CrossAccountId,
+		to: T::CrossAccountId,
+		data: CreateItemData,
+	) -> DispatchResultWithPostInfo;
+	fn create_multiple_items(
+		&self,
+		sender: T::CrossAccountId,
+		to: T::CrossAccountId,
+		data: Vec<CreateItemData>,
+	) -> DispatchResultWithPostInfo;
+	fn burn_item(
+		&self,
+		sender: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo;
+
+	fn transfer(
+		&self,
+		sender: T::CrossAccountId,
+		to: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo;
+	fn approve(
+		&self,
+		sender: T::CrossAccountId,
+		spender: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo;
+	fn transfer_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		to: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo;
+
+	fn set_variable_metadata(
+		&self,
+		sender: T::CrossAccountId,
+		token: TokenId,
+		data: Vec<u8>,
+	) -> DispatchResultWithPostInfo;
+
+	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;
+	fn token_exists(&self, token: TokenId) -> bool;
+
+	fn token_owner(&self, token: TokenId) -> T::CrossAccountId;
+	fn const_metadata(&self, token: TokenId) -> Vec<u8>;
+	fn variable_metadata(&self, token: TokenId) -> Vec<u8>;
+
+	/// How many tokens collection contains (Applicable to nonfungible/refungible)
+	fn collection_tokens(&self) -> u32;
+	/// Amount of different tokens account has (Applicable to nonfungible/refungible)
+	fn account_balance(&self, account: T::CrossAccountId) -> u32;
+	/// Amount of specific token account have (Applicable to fungible/refungible)
+	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;
+	fn allowance(
+		&self,
+		sender: T::CrossAccountId,
+		spender: T::CrossAccountId,
+		token: TokenId,
+	) -> u128;
+}
+
+// Flexible enough for implementing CommonCollectionOperations
+pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {
+	let post_info = PostDispatchInfo {
+		actual_weight: Some(weight),
+		pays_fee: Pays::Yes,
+	};
+	match res {
+		Ok(()) => Ok(post_info),
+		Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),
+	}
+}
deletedpallets/nft/src/eth/account.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/account.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use crate::Config;
-use codec::{Encode, EncodeLike, Decode};
-use sp_core::crypto::AccountId32;
-use primitive_types::H160;
-use core::cmp::Ordering;
-use serde::{Serialize, Deserialize};
-use pallet_evm::AddressMapping;
-use sp_std::vec::Vec;
-use sp_std::clone::Clone;
-
-pub trait CrossAccountId<AccountId>:
-	Encode + EncodeLike + Decode + Clone + PartialEq + Ord + core::fmt::Debug
-// +
-// Serialize + Deserialize<'static>
-{
-	fn as_sub(&self) -> &AccountId;
-	fn as_eth(&self) -> &H160;
-
-	fn from_sub(account: AccountId) -> Self;
-	fn from_eth(account: H160) -> Self;
-}
-
-#[derive(Eq, Serialize, Deserialize)]
-pub struct BasicCrossAccountId<T: Config> {
-	/// If true - then ethereum is canonical encoding
-	from_ethereum: bool,
-	substrate: T::AccountId,
-	ethereum: H160,
-}
-
-impl<T: Config> core::fmt::Debug for BasicCrossAccountId<T> {
-	fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
-		if self.from_ethereum {
-			fmt.debug_tuple("CrossAccountId::Ethereum")
-				.field(&self.ethereum)
-				.finish()
-		} else {
-			fmt.debug_tuple("CrossAccountId::Substrate")
-				.field(&self.substrate)
-				.finish()
-		}
-	}
-}
-
-impl<T: Config> PartialOrd for BasicCrossAccountId<T> {
-	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-		Some(self.substrate.cmp(&other.substrate))
-	}
-}
-
-impl<T: Config> Ord for BasicCrossAccountId<T> {
-	fn cmp(&self, other: &Self) -> Ordering {
-		self.partial_cmp(other)
-			.expect("substrate account is total ordered")
-	}
-}
-
-impl<T: Config> PartialEq for BasicCrossAccountId<T> {
-	fn eq(&self, other: &Self) -> bool {
-		if self.from_ethereum == other.from_ethereum {
-			self.substrate == other.substrate && self.ethereum == other.ethereum
-		} else if self.from_ethereum {
-			// ethereum is canonical encoding, but we need to compare derived address
-			self.substrate == other.substrate
-		} else {
-			self.ethereum == other.ethereum
-		}
-	}
-}
-impl<T: Config> Clone for BasicCrossAccountId<T> {
-	fn clone(&self) -> Self {
-		Self {
-			from_ethereum: self.from_ethereum,
-			substrate: self.substrate.clone(),
-			ethereum: self.ethereum,
-		}
-	}
-}
-impl<T: Config> Encode for BasicCrossAccountId<T> {
-	fn encode(&self) -> Vec<u8> {
-		let as_result = if !self.from_ethereum {
-			Ok(self.substrate.clone())
-		} else {
-			Err(self.ethereum)
-		};
-		as_result.encode()
-	}
-}
-impl<T: Config> EncodeLike for BasicCrossAccountId<T> {}
-impl<T: Config> Decode for BasicCrossAccountId<T> {
-	fn decode<I>(input: &mut I) -> Result<Self, codec::Error>
-	where
-		I: codec::Input,
-	{
-		Ok(match <Result<T::AccountId, H160>>::decode(input)? {
-			Ok(s) => Self::from_sub(s),
-			Err(e) => Self::from_eth(e),
-		})
-	}
-}
-impl<T: Config> CrossAccountId<T::AccountId> for BasicCrossAccountId<T> {
-	fn as_sub(&self) -> &T::AccountId {
-		&self.substrate
-	}
-	fn as_eth(&self) -> &H160 {
-		&self.ethereum
-	}
-	fn from_sub(substrate: T::AccountId) -> Self {
-		Self {
-			ethereum: T::EvmBackwardsAddressMapping::from_account_id(substrate.clone()),
-			substrate,
-			from_ethereum: false,
-		}
-	}
-	fn from_eth(ethereum: H160) -> Self {
-		Self {
-			ethereum,
-			substrate: T::EvmAddressMapping::into_account_id(ethereum),
-			from_ethereum: true,
-		}
-	}
-}
-
-pub trait EvmBackwardsAddressMapping<AccountId> {
-	fn from_account_id(account_id: AccountId) -> H160;
-}
-
-/// Should have same mapping as EnsureAddressTruncated
-pub struct MapBackwardsAddressTruncated;
-impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
-	fn from_account_id(account_id: AccountId32) -> H160 {
-		let mut out = [0; 20];
-		out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
-		H160(out)
-	}
-}
deletedpallets/nft/src/eth/erc.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/erc.rs
+++ /dev/null
@@ -1,496 +0,0 @@
-use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
-use evm_coder::{ToLog, execution::Result, generate_stubgen, solidity, solidity_interface, types::*};
-use nft_data_structs::{CreateItemData, CreateNftData};
-use core::convert::TryInto;
-use crate::{
-	Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,
-	ItemListIndex,
-};
-use frame_support::storage::{StorageMap, StorageDoubleMap};
-use pallet_evm::AddressMapping;
-use pallet_evm_coder_substrate::dispatch_to_evm;
-use super::account::CrossAccountId;
-use sp_std::{vec, vec::Vec};
-
-#[solidity_interface(name = "ERC165")]
-impl<T: Config> CollectionHandle<T> {
-	fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {
-		Ok(match self.mode {
-			CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),
-			CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),
-			_ => false,
-		})
-	}
-}
-
-#[solidity_interface(name = "InlineNameSymbol")]
-impl<T: Config> CollectionHandle<T> {
-	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())
-	}
-}
-
-#[solidity_interface(name = "InlineTotalSupply")]
-impl<T: Config> CollectionHandle<T> {
-	fn total_supply(&self) -> Result<uint256> {
-		// TODO: we do not track total amount of all tokens
-		Ok(0.into())
-	}
-}
-
-#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
-impl<T: Config> CollectionHandle<T> {
-	#[solidity(rename_selector = "tokenURI")]
-	fn token_uri(&self, token_id: uint256) -> Result<string> {
-		let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
-		Ok(string::from_utf8_lossy(
-			&<NftItemList<T>>::get(self.id, token_id)
-				.ok_or("token not found")?
-				.const_data,
-		)
-		.into())
-	}
-}
-
-#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]
-impl<T: Config> CollectionHandle<T> {
-	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())
-	}
-}
-
-#[derive(ToLog)]
-pub enum ERC721Events {
-	Transfer {
-		#[indexed]
-		from: address,
-		#[indexed]
-		to: address,
-		#[indexed]
-		token_id: uint256,
-	},
-	Approval {
-		#[indexed]
-		owner: address,
-		#[indexed]
-		approved: address,
-		#[indexed]
-		token_id: uint256,
-	},
-	#[allow(dead_code)]
-	ApprovalForAll {
-		#[indexed]
-		owner: address,
-		#[indexed]
-		operator: address,
-		approved: bool,
-	},
-}
-
-#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]
-impl<T: Config> CollectionHandle<T> {
-	#[solidity(rename_selector = "balanceOf")]
-	fn balance_of_nft(&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())
-	}
-}
-
-#[solidity_interface(name = "ERC721Burnable")]
-impl<T: Config> CollectionHandle<T> {
-	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
-
-		<Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;
-		Ok(())
-	}
-}
-
-#[derive(ToLog)]
-pub enum ERC721MintableEvents {
-	#[allow(dead_code)]
-	MintingFinished {},
-}
-
-#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
-impl<T: Config> CollectionHandle<T> {
-	fn minting_finished(&self) -> Result<bool> {
-		Ok(false)
-	}
-
-	fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let to = T::CrossAccountId::from_eth(to);
-		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
-		if <ItemListIndex>::get(self.id)
-			.checked_add(1)
-			.ok_or("item id overflow")?
-			!= token_id
-		{
-			return Err("item id should be next".into());
-		}
-
-		<Module<T>>::create_item_internal(
-			&caller,
-			&self,
-			&to,
-			CreateItemData::NFT(CreateNftData {
-				const_data: vec![].try_into().unwrap(),
-				variable_data: vec![].try_into().unwrap(),
-			}),
-		)
-		.map_err(|_| "mint error")?;
-		Ok(true)
-	}
-
-	#[solidity(rename_selector = "mintWithTokenURI")]
-	fn mint_with_token_uri(
-		&mut self,
-		caller: caller,
-		to: address,
-		token_id: uint256,
-		token_uri: string,
-	) -> Result<bool> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let to = T::CrossAccountId::from_eth(to);
-		let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
-		if <ItemListIndex>::get(self.id)
-			.checked_add(1)
-			.ok_or("item id overflow")?
-			!= token_id
-		{
-			return Err("item id should be next".into());
-		}
-
-		<Module<T>>::create_item_internal(
-			&caller,
-			&self,
-			&to,
-			CreateItemData::NFT(CreateNftData {
-				const_data: Vec::<u8>::from(token_uri)
-					.try_into()
-					.map_err(|_| "token uri is too long")?,
-				variable_data: vec![].try_into().unwrap(),
-			}),
-		)
-		.map_err(|_| "mint error")?;
-		Ok(true)
-	}
-
-	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
-		Err("not implementable".into())
-	}
-}
-
-#[solidity_interface(name = "ERC721UniqueExtensions")]
-impl<T: Config> CollectionHandle<T> {
-	#[solidity(rename_selector = "transfer")]
-	fn transfer_nft(
-		&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(())
-	}
-
-	fn next_token_id(&self) -> Result<uint256> {
-		Ok(ItemListIndex::get(self.id)
-			.checked_add(1)
-			.ok_or("item id overflow")?
-			.into())
-	}
-
-	fn set_variable_metadata(
-		&mut self,
-		caller: caller,
-		token_id: uint256,
-		data: bytes,
-	) -> Result<void> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
-
-		<Module<T>>::set_variable_meta_data_internal(&caller, self, token_id, data)
-			.map_err(dispatch_to_evm::<T>)?;
-		Ok(())
-	}
-
-	fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {
-		let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
-
-		<Module<T>>::get_variable_metadata(self, token_id).map_err(dispatch_to_evm::<T>)
-	}
-
-	fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let to = T::CrossAccountId::from_eth(to);
-		let mut expected_index = <ItemListIndex>::get(self.id)
-			.checked_add(1)
-			.ok_or("item id overflow")?;
-
-		let total_tokens = token_ids.len();
-		for id in token_ids.into_iter() {
-			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
-			if id != expected_index {
-				return Err("item id should be next".into());
-			}
-			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
-		}
-
-		let data = (0..total_tokens)
-			.map(|_| {
-				CreateItemData::NFT(CreateNftData {
-					const_data: vec![].try_into().unwrap(),
-					variable_data: vec![].try_into().unwrap(),
-				})
-			})
-			.collect();
-
-		<Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
-			.map_err(dispatch_to_evm::<T>)?;
-		Ok(true)
-	}
-
-	#[solidity(rename_selector = "mintBulkWithTokenURI")]
-	fn mint_bulk_with_token_uri(
-		&mut self,
-		caller: caller,
-		to: address,
-		tokens: Vec<(uint256, string)>,
-	) -> Result<bool> {
-		let caller = T::CrossAccountId::from_eth(caller);
-		let to = T::CrossAccountId::from_eth(to);
-		let mut expected_index = <ItemListIndex>::get(self.id)
-			.checked_add(1)
-			.ok_or("item id overflow")?;
-
-		let mut data = Vec::with_capacity(tokens.len());
-		for (id, token_uri) in tokens {
-			let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
-			if id != expected_index {
-				panic!("item id should be next ({}) but got {}", expected_index, id);
-			}
-			expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
-
-			data.push(CreateItemData::NFT(CreateNftData {
-				const_data: Vec::<u8>::from(token_uri)
-					.try_into()
-					.map_err(|_| "token uri is too long")?,
-				variable_data: vec![].try_into().unwrap(),
-			}));
-		}
-
-		<Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
-			.map_err(dispatch_to_evm::<T>)?;
-		Ok(true)
-	}
-}
-
-#[solidity_interface(
-	name = "UniqueNFT",
-	is(
-		ERC165,
-		ERC721,
-		ERC721Metadata,
-		ERC721Enumerable,
-		ERC721UniqueExtensions,
-		ERC721Mintable,
-		ERC721Burnable,
-	)
-)]
-impl<T: Config> CollectionHandle<T> {}
-
-#[derive(ToLog)]
-pub enum ERC20Events {
-	Transfer {
-		#[indexed]
-		from: address,
-		#[indexed]
-		to: address,
-		value: uint256,
-	},
-	Approval {
-		#[indexed]
-		owner: address,
-		#[indexed]
-		spender: address,
-		value: uint256,
-	},
-}
-
-#[solidity_interface(
-	name = "ERC20",
-	inline_is(InlineNameSymbol, InlineTotalSupply),
-	events(ERC20Events)
-)]
-impl<T: Config> CollectionHandle<T> {
-	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)
-	}
-	#[solidity(rename_selector = "transferFrom")]
-	fn transfer_from_fungible(
-		&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)
-	}
-	#[solidity(rename_selector = "approve")]
-	fn approve_fungible(
-		&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())
-	}
-}
-
-#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
-impl<T: Config> CollectionHandle<T> {}
-
-// Not a tests, but code generators
-generate_stubgen!(nft_impl, UniqueNFTCall, true);
-generate_stubgen!(nft_iface, UniqueNFTCall, false);
-
-generate_stubgen!(fungible_impl, UniqueFungibleCall, true);
-generate_stubgen!(fungible_iface, UniqueFungibleCall, false);
modifiedpallets/nft/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/nft/src/eth/mod.rs
+++ b/pallets/nft/src/eth/mod.rs
@@ -1,86 +1,21 @@
-pub mod account;
-pub mod erc;
 pub mod sponsoring;
 
-use pallet_evm_coder_substrate::call_internal;
+use pallet_common::{
+	CollectionById,
+	erc::CommonEvmHandler,
+	eth::{map_eth_to_id, map_eth_to_token_id},
+};
+use pallet_fungible::FungibleHandle;
+use pallet_nonfungible::NonfungibleHandle;
+use pallet_refungible::{RefungibleHandle, erc::RefungibleTokenHandle};
 use sp_std::borrow::ToOwned;
 use sp_std::vec::Vec;
 use pallet_evm::{PrecompileOutput};
 use sp_core::{H160, U256};
-use frame_support::storage::StorageMap;
-use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};
-use erc::{UniqueFungibleCall, UniqueNFTCall};
+use crate::{CollectionMode, Config, dispatch::Dispatched};
+use pallet_common::CollectionHandle;
 
 pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);
-
-// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection
-// TODO: Unhardcode prefix
-const ETH_ACCOUNT_PREFIX: [u8; 16] = [
-	0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,
-];
-
-fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {
-	if eth[0..16] != ETH_ACCOUNT_PREFIX {
-		return None;
-	}
-	let mut id_bytes = [0; 4];
-	id_bytes.copy_from_slice(&eth[16..20]);
-	Some(u32::from_be_bytes(id_bytes))
-}
-pub fn collection_id_to_address(id: u32) -> H160 {
-	let mut out = [0; 20];
-	out[0..16].copy_from_slice(&ETH_ACCOUNT_PREFIX);
-	out[16..20].copy_from_slice(&u32::to_be_bytes(id));
-	H160(out)
-}
-
-/*
-fn call_internal<T: Config>(
-	collection: &mut CollectionHandle<T>,
-	caller: caller,
-	method_id: u32,
-	mut input: AbiReader,
-	value: U256,
-) -> Result<Option<AbiWriter>> {
-	match collection.mode.clone() {
-		CollectionMode::Fungible(_) => {
-			call_internal();
-			let call = match UniqueFungibleCall::parse(method_id, &mut input)? {
-				Some(v) => v,
-				None => {
-					#[cfg(feature = "std")]
-					{
-						println!("Method not found");
-					}
-					return Ok(None);
-				}
-			};
-			Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(
-				collection,
-				Msg {
-					call,
-					caller,
-					value,
-				},
-			)?))
-		}
-		CollectionMode::NFT => {
-			let call = match UniqueNFTCall::parse(method_id, &mut input)? {
-				Some(v) => v,
-				None => return Ok(None),
-			};
-			Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(
-				collection,
-				Msg {
-					call,
-					caller,
-					value,
-				},
-			)?))
-		}
-		_ => 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 {
@@ -92,20 +27,26 @@
 			.unwrap_or(false)
 	}
 	fn get_code(target: &H160) -> Option<Vec<u8>> {
-		map_eth_to_id(target)
-			.and_then(<CollectionById<T>>::get)
-			.map(|collection| {
+		if let Some(collection_id) = map_eth_to_id(target) {
+			let collection = <CollectionById<T>>::get(collection_id)?;
+			Some(
 				match collection.mode {
-					CollectionMode::NFT => include_bytes!("stubs/UniqueNFT.raw") as &[u8],
-					CollectionMode::Fungible(_) => {
-						include_bytes!("stubs/UniqueFungible.raw") as &[u8]
-					}
-					CollectionMode::ReFungible => {
-						include_bytes!("stubs/UniqueRefungible.raw") as &[u8]
-					}
+					CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,
+					CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,
+					CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,
 				}
-				.to_owned()
-			})
+				.to_owned(),
+			)
+		} else if let Some((collection_id, _token_id)) = map_eth_to_token_id(target) {
+			let collection = <CollectionById<T>>::get(collection_id)?;
+			if collection.mode != CollectionMode::ReFungible {
+				return None;
+			}
+			// TODO: check token existence
+			Some(<RefungibleTokenHandle<T>>::CODE.to_owned())
+		} else {
+			None
+		}
 	}
 	fn call(
 		source: &H160,
@@ -114,17 +55,26 @@
 		input: &[u8],
 		value: U256,
 	) -> Option<PrecompileOutput> {
-		let mut collection = map_eth_to_id(target)
-			.and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;
-		let result = match collection.mode {
-			CollectionMode::NFT => {
-				call_internal::<UniqueNFTCall, _>(*source, &mut collection, value, input)
+		if let Some(collection_id) = map_eth_to_id(target) {
+			let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+			let dispatched = Dispatched::dispatch(collection);
+
+			match dispatched {
+				Dispatched::Fungible(h) => h.call(source, input, value),
+				Dispatched::Nonfungible(h) => h.call(source, input, value),
+				Dispatched::Refungible(h) => h.call(source, input, value),
 			}
-			CollectionMode::Fungible(_) => {
-				call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)
+		} else if let Some((collection_id, token_id)) = map_eth_to_token_id(target) {
+			let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+			if collection.mode != CollectionMode::ReFungible {
+				return None;
 			}
-			_ => return None,
-		};
-		collection.recorder.evm_to_precompile_output(result)
+
+			let handle = RefungibleHandle::cast(collection);
+			// TODO: check token existence
+			RefungibleTokenHandle(handle, token_id).call(source, input, value)
+		} else {
+			None
+		}
 	}
 }
modifiedpallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth
1//! Implements EVM sponsoring logic via OnChargeEVMTransaction1//! Implements EVM sponsoring logic via OnChargeEVMTransaction
22
3use crate::{3use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};
4 Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,
5 eth::{account::EvmBackwardsAddressMapping, map_eth_to_id},
6};
7use evm_coder::{Call, abi::AbiReader};4use evm_coder::{Call, abi::AbiReader};
8use frame_support::{5use frame_support::{
9 storage::{StorageMap, StorageDoubleMap},6 storage::{StorageDoubleMap},
10};7};
8use pallet_common::eth::map_eth_to_id;
11use sp_core::H160;9use sp_core::H160;
12use sp_std::prelude::*;10use sp_std::prelude::*;
13use up_sponsorship::SponsorshipHandler;11use up_sponsorship::SponsorshipHandler;
14use super::{12use core::marker::PhantomData;
15 account::CrossAccountId,
16 erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},
17};
18use core::convert::TryInto;13use core::convert::TryInto;
14use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
15use pallet_common::{
16 CollectionById,
17 account::{CrossAccountId, EvmBackwardsAddressMapping},
18};
19
19use core::marker::PhantomData;20use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
20use nft_data_structs::{NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};21use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
2122
22struct AnyError;23struct AnyError;
2324
24fn try_sponsor<T: Config>(25fn try_sponsor<T: Config>(
25 caller: &H160,26 caller: &H160,
26 collection_id: u32,27 collection_id: CollectionId,
27 collection: &Collection<T>,28 collection: &Collection<T>,
28 call: &[u8],29 call: &[u8],
29) -> Result<(), AnyError> {30) -> Result<(), AnyError> {
deletedpallets/nft/src/eth/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

deletedpallets/nft/src/eth/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/UniqueFungible.sol
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-// Common stubs holder
-contract Dummy {
-	uint8 dummy;
-	string stub_error = "this contract is implemented in native";
-}
-
-// Inline
-contract ERC20Events {
-	event Transfer(address indexed from, address indexed to, uint256 value);
-	event Approval(
-		address indexed owner,
-		address indexed spender,
-		uint256 value
-	);
-}
-
-// Inline
-contract InlineNameSymbol is Dummy {
-	// Selector: name() 06fdde03
-	function name() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	// Selector: symbol() 95d89b41
-	function symbol() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-}
-
-// Inline
-contract InlineTotalSupply is Dummy {
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-}
-
-contract ERC165 is Dummy {
-	// Selector: supportsInterface(bytes4) 01ffc9a7
-	function supportsInterface(uint32 interfaceId) public view returns (bool) {
-		require(false, stub_error);
-		interfaceId;
-		dummy;
-		return false;
-	}
-}
-
-contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
-	// Selector: decimals() 313ce567
-	function decimals() public view returns (uint8) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
-		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
-	}
-
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) public returns (bool) {
-		require(false, stub_error);
-		from;
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address spender, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		spender;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: allowance(address,address) dd62ed3e
-	function allowance(address owner, address spender)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		spender;
-		dummy;
-		return 0;
-	}
-}
-
-contract UniqueFungible is Dummy, ERC165, ERC20 {}
deletedpallets/nft/src/eth/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

deletedpallets/nft/src/eth/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/UniqueNFT.sol
+++ /dev/null
@@ -1,326 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-// Anonymous struct
-struct Tuple0 {
-	uint256 field_0;
-	string field_1;
-}
-
-// Common stubs holder
-contract Dummy {
-	uint8 dummy;
-	string stub_error = "this contract is implemented in native";
-}
-
-// Inline
-contract ERC721Events {
-	event Transfer(
-		address indexed from,
-		address indexed to,
-		uint256 indexed tokenId
-	);
-	event Approval(
-		address indexed owner,
-		address indexed approved,
-		uint256 indexed tokenId
-	);
-	event ApprovalForAll(
-		address indexed owner,
-		address indexed operator,
-		bool approved
-	);
-}
-
-// Inline
-contract ERC721MintableEvents {
-	event MintingFinished();
-}
-
-// Inline
-contract InlineNameSymbol is Dummy {
-	// Selector: name() 06fdde03
-	function name() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	// Selector: symbol() 95d89b41
-	function symbol() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-}
-
-// Inline
-contract InlineTotalSupply is Dummy {
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-}
-
-contract ERC165 is Dummy {
-	// Selector: supportsInterface(bytes4) 01ffc9a7
-	function supportsInterface(uint32 interfaceId) public view returns (bool) {
-		require(false, stub_error);
-		interfaceId;
-		dummy;
-		return false;
-	}
-}
-
-contract ERC721 is Dummy, ERC165, ERC721Events {
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
-		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
-	}
-
-	// Selector: ownerOf(uint256) 6352211e
-	function ownerOf(uint256 tokenId) public view returns (address) {
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
-	}
-
-	// Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
-	function safeTransferFromWithData(
-		address from,
-		address to,
-		uint256 tokenId,
-		bytes memory data
-	) public {
-		require(false, stub_error);
-		from;
-		to;
-		tokenId;
-		data;
-		dummy = 0;
-	}
-
-	// Selector: safeTransferFrom(address,address,uint256) 42842e0e
-	function safeTransferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
-		require(false, stub_error);
-		from;
-		to;
-		tokenId;
-		dummy = 0;
-	}
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 tokenId
-	) public {
-		require(false, stub_error);
-		from;
-		to;
-		tokenId;
-		dummy = 0;
-	}
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address approved, uint256 tokenId) public {
-		require(false, stub_error);
-		approved;
-		tokenId;
-		dummy = 0;
-	}
-
-	// Selector: setApprovalForAll(address,bool) a22cb465
-	function setApprovalForAll(address operator, bool approved) public {
-		require(false, stub_error);
-		operator;
-		approved;
-		dummy = 0;
-	}
-
-	// Selector: getApproved(uint256) 081812fc
-	function getApproved(uint256 tokenId) public view returns (address) {
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
-	}
-
-	// Selector: isApprovedForAll(address,address) e985e9c5
-	function isApprovedForAll(address owner, address operator)
-		public
-		view
-		returns (address)
-	{
-		require(false, stub_error);
-		owner;
-		operator;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
-	}
-}
-
-contract ERC721Burnable is Dummy {
-	// Selector: burn(uint256) 42966c68
-	function burn(uint256 tokenId) public {
-		require(false, stub_error);
-		tokenId;
-		dummy = 0;
-	}
-}
-
-contract ERC721Enumerable is Dummy, InlineTotalSupply {
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-}
-
-contract ERC721Metadata is Dummy, InlineNameSymbol {
-	// Selector: tokenURI(uint256) c87b56dd
-	function tokenURI(uint256 tokenId) public view returns (string memory) {
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return "";
-	}
-}
-
-contract ERC721Mintable is Dummy, ERC721MintableEvents {
-	// Selector: mintingFinished() 05d2035b
-	function mintingFinished() public view returns (bool) {
-		require(false, stub_error);
-		dummy;
-		return false;
-	}
-
-	// Selector: mint(address,uint256) 40c10f19
-	function mint(address to, uint256 tokenId) public returns (bool) {
-		require(false, stub_error);
-		to;
-		tokenId;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
-	function mintWithTokenURI(
-		address to,
-		uint256 tokenId,
-		string memory tokenUri
-	) public returns (bool) {
-		require(false, stub_error);
-		to;
-		tokenId;
-		tokenUri;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: finishMinting() 7d64bcb4
-	function finishMinting() public returns (bool) {
-		require(false, stub_error);
-		dummy = 0;
-		return false;
-	}
-}
-
-contract ERC721UniqueExtensions is Dummy {
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 tokenId) public {
-		require(false, stub_error);
-		to;
-		tokenId;
-		dummy = 0;
-	}
-
-	// Selector: nextTokenId() 75794a3c
-	function nextTokenId() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: setVariableMetadata(uint256,bytes) d4eac26d
-	function setVariableMetadata(uint256 tokenId, bytes memory data) public {
-		require(false, stub_error);
-		tokenId;
-		data;
-		dummy = 0;
-	}
-
-	// Selector: getVariableMetadata(uint256) e6c5ce6f
-	function getVariableMetadata(uint256 tokenId)
-		public
-		view
-		returns (bytes memory)
-	{
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return hex"";
-	}
-
-	// Selector: mintBulk(address,uint256[]) 44a9945e
-	function mintBulk(address to, uint256[] memory tokenIds)
-		public
-		returns (bool)
-	{
-		require(false, stub_error);
-		to;
-		tokenIds;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
-		public
-		returns (bool)
-	{
-		require(false, stub_error);
-		to;
-		tokens;
-		dummy = 0;
-		return false;
-	}
-}
-
-contract UniqueNFT is
-	Dummy,
-	ERC165,
-	ERC721,
-	ERC721Metadata,
-	ERC721Enumerable,
-	ERC721UniqueExtensions,
-	ERC721Mintable,
-	ERC721Burnable
-{}
deletedpallets/nft/src/eth/stubs/UniqueRefungible.rawdiffbeforeafterboth
--- a/pallets/nft/src/eth/stubs/UniqueRefungible.raw
+++ /dev/null
@@ -1 +0,0 @@
-TODO
\ No newline at end of file
modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
--- a/pallets/nft/src/lib.rs
+++ b/pallets/nft/src/lib.rs
@@ -69,164 +69,29 @@
 decl_error! {
 	/// Error for non-fungible-token module.
 	pub enum Error for Module<T: Config> {
-		/// Total collections bound exceeded.
-		TotalCollectionsLimitExceeded,
 		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.
 		CollectionDecimalPointLimitExceeded,
-		/// Collection name can not be longer than 63 char.
-		CollectionNameLimitExceeded,
-		/// Collection description can not be longer than 255 char.
-		CollectionDescriptionLimitExceeded,
-		/// Token prefix can not be longer than 15 char.
-		CollectionTokenPrefixLimitExceeded,
-		/// This collection does not exist.
-		CollectionNotFound,
-		/// Item not exists.
-		TokenNotFound,
-		/// Admin not found
-		AdminNotFound,
-		/// Arithmetic calculation overflow.
-		NumOverflow,
-		/// Account already has admin role.
-		AlreadyAdmin,
-		/// You do not own this collection.
-		NoPermission,
 		/// This address is not set as sponsor, use setCollectionSponsor first.
 		ConfirmUnsetSponsorFail,
-		/// Collection is not in mint mode.
-		PublicMintingNotAllowed,
-		/// Sender parameter and item owner must be equal.
-		MustBeTokenOwner,
-		/// Item balance not enough.
-		TokenValueTooLow,
-		/// Size of item is too large.
-		NftSizeLimitExceeded,
-		/// No approve found
-		ApproveNotFound,
-		/// Requested value more than approved.
-		TokenValueNotEnough,
-		/// Only approved addresses can call this method.
-		ApproveRequired,
-		/// Address is not in white list.
-		AddresNotInWhiteList,
-		/// Number of collection admins bound exceeded.
-		CollectionAdminsLimitExceeded,
-		/// Owned tokens by a single address bound exceeded.
-		AddressOwnershipLimitExceeded,
 		/// Length of items properties must be greater than 0.
 		EmptyArgument,
-		/// const_data exceeded data limit.
-		TokenConstDataLimitExceeded,
-		/// variable_data exceeded data limit.
-		TokenVariableDataLimitExceeded,
-		/// Not NFT item data used to mint in NFT collection.
-		NotNftDataUsedToMintNftCollectionToken,
-		/// Not Fungible item data used to mint in Fungible collection.
-		NotFungibleDataUsedToMintFungibleCollectionToken,
-		/// Not Re Fungible item data used to mint in Re Fungible collection.
-		NotReFungibleDataUsedToMintReFungibleCollectionToken,
-		/// Unexpected collection type.
-		UnexpectedCollectionType,
-		/// Can't store metadata in fungible tokens.
-		CantStoreMetadataInFungibleTokens,
-		/// Collection token limit exceeded
-		CollectionTokenLimitExceeded,
-		/// Account token limit exceeded per collection
-		AccountTokenLimitExceeded,
 		/// Collection limit bounds per collection exceeded
 		CollectionLimitBoundsExceeded,
 		/// Tried to enable permissions which are only permitted to be disabled
 		OwnerPermissionsCantBeReverted,
-		/// Schema data size limit bound exceeded
-		SchemaDataLimitExceeded,
-		/// Maximum refungibility exceeded
-		WrongRefungiblePieces,
-		/// createRefungible should be called with one owner
-		BadCreateRefungibleCall,
-		/// Gas limit exceeded
-		OutOfGas,
-		/// Metadata update denied by collection settings
-		MetadataUpdateDenied,
-		/// Metadata update flag become unmutable with None option
-		MetadataFlagFrozen,
-		/// Collection settings not allowing items transferring
-		TransferNotAllowed,
-		/// Can't transfer tokens to ethereum zero address
-		AddressIsZero,
 	}
-}
-
-#[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>,
-	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,
-			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 log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
-		self.recorder.log_sub(log)
-	}
-	#[allow(dead_code)]
-	fn consume_gas(&self, gas: u64) -> DispatchResult {
-		self.recorder.consume_gas_sub(gas)
-	}
-	fn consume_sload(&self) -> DispatchResult {
-		self.recorder.consume_sload_sub()
-	}
-	fn consume_sstore(&self) -> DispatchResult {
-		self.recorder.consume_sstore_sub()
-	}
-	pub fn submit_logs(self) -> DispatchResult {
-		self.recorder.submit_logs()
-	}
-	pub fn save(self) -> DispatchResult {
-		self.recorder.submit_logs()?;
-		<CollectionById<T>>::insert(self.id, self.collection);
-		Ok(())
-	}
-}
-impl<T: Config> Deref for CollectionHandle<T> {
-	type Target = Collection<T>;
-
-	fn deref(&self) -> &Self::Target {
-		&self.collection
-	}
-}
-
-impl<T: Config> DerefMut for CollectionHandle<T> {
-	fn deref_mut(&mut self) -> &mut Self::Target {
-		&mut self.collection
-	}
-}
-
-pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {
-	type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
-
+pub trait Config:
+	system::Config
+	+ pallet_evm_coder_substrate::Config
+	+ pallet_common::Config
+	+ pallet_nonfungible::Config
+	+ pallet_refungible::Config
+	+ pallet_fungible::Config
+	+ Sized
+{
 	/// Weight information for extrinsics in this pallet.
 	type WeightInfo: WeightInfo;
-
-	type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
-	type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
-
-	type CrossAccountId: CrossAccountId<Self::AccountId>;
-	type Currency: Currency<Self::AccountId>;
-	type CollectionCreationPrice: Get<
-		<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
-	>;
-	type TreasuryAccountId: Get<Self::AccountId>;
 }
 
 type SelfWeightOf<T> = <T as Config>::WeightInfo;
@@ -293,56 +158,10 @@
 	trait Store for Module<T: Config> as Nft {
 
 		//#region Private members
-		/// Id of next collection
-		CreatedCollectionCount: u32;
 		/// Used for migrations
 		ChainVersion: u64;
-		/// Id of last collection token
-		/// Collection id (controlled?1)
-		ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;
-		//#endregion
-
-		//#region Bound counters
-		/// Amount of collections destroyed, used for total amount tracking with
-		/// CreatedCollectionCount
-		DestroyedCollectionCount: u32;
-		//#endregion
-
-		//#region Basic collections
-		/// Collection info
-		/// Collection id (controlled?1)
-		pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;
-		/// List of collection admins
-		/// Collection id (controlled?2)
-		pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;
-		/// Whitelisted collection users
-		/// Collection id (controlled?2), user id (controlled?3)
-		pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;
 		//#endregion
 
-		/// How many of collection items user have
-		/// Collection id (controlled?2), account id (real)
-		pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;
-
-		/// Amount of items which spender can transfer out of owners account (via transferFrom)
-		/// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))
-		/// TODO: Off chain worker should remove from this map when token gets removed
-		pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;
-
-		//#region Item collections
-		/// Collection id (controlled?2), token id (controlled?1)
-		pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;
-		/// Collection id (controlled?2), owner (controlled?2)
-		pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;
-		/// Collection id (controlled?2), token id (controlled?1)
-		pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;
-		//#endregion
-
-		//#region Index list
-		/// Collection id (controlled?2), tokens owner (controlled?2)
-		pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;
-		//#endregion
-
 		//#region Tokens transfer rate limit baskets
 		/// (Collection id (controlled?2), who created (real))
 		/// TODO: Off chain worker should remove from this map when collection gets removed
@@ -359,97 +178,13 @@
 		/// Collection id (controlled?2), token id (controlled?2)
 		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;
 	}
-	add_extra_genesis {
-		build(|config: &GenesisConfig<T>| {
-			// Modification of storage
-			for (_num, _c) in &config.collection_id {
-				<Module<T>>::init_collection(_c);
-			}
-
-			for (_num, _c, _i) in &config.nft_item_id {
-				<Module<T>>::init_nft_token(*_c, _i);
-			}
-
-			for (collection_id, account_id, fungible_item) in &config.fungible_item_id {
-				<Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);
-			}
-
-			for (_num, _c, _i) in &config.refungible_item_id {
-				<Module<T>>::init_refungible_token(*_c, _i);
-			}
-		})
-	}
 }
-
-decl_event!(
-	pub enum Event<T>
-	where
-		AccountId = <T as frame_system::Config>::AccountId,
-		CrossAccountId = <T as Config>::CrossAccountId,
-	{
-		/// New collection was created
-		///
-		/// # Arguments
-		///
-		/// * collection_id: Globally unique identifier of newly created collection.
-		///
-		/// * mode: [CollectionMode] converted into u8.
-		///
-		/// * account_id: Collection owner.
-		CollectionCreated(CollectionId, u8, AccountId),
-
-		/// New item was created.
-		///
-		/// # Arguments
-		///
-		/// * collection_id: Id of the collection where item was created.
-		///
-		/// * item_id: Id of an item. Unique within the collection.
-		///
-		/// * recipient: Owner of newly created item
-		ItemCreated(CollectionId, TokenId, CrossAccountId),
-
-		/// Collection item was burned.
-		///
-		/// # Arguments
-		///
-		/// collection_id.
-		///
-		/// item_id: Identifier of burned NFT.
-		ItemDestroyed(CollectionId, TokenId),
 
-		/// Item was transferred
-		///
-		/// * collection_id: Id of collection to which item is belong
-		///
-		/// * item_id: Id of an item
-		///
-		/// * sender: Original owner of item
-		///
-		/// * recipient: New owner of item
-		///
-		/// * amount: Always 1 for NFT
-		Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),
-
-		/// * collection_id
-		///
-		/// * item_id
-		///
-		/// * sender
-		///
-		/// * spender
-		///
-		/// * amount
-		Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),
-	}
-);
-
 decl_module! {
 	pub struct Module<T: Config> for enum Call
 	where
 		origin: T::Origin
 	{
-		fn deposit_event() = default;
 		const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;
 		type Error = Error<T>;