git.delta.rocks / unique-network / refs/commits / 6d7ceb40a19f

difftreelog

refactor split nonfungible into its own pallet

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

8 files changed

addedpallets/nonfungible/Cargo.tomldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "pallet-nonfungible"
+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' }
+pallet-common = { default-features = false, path = '../common' }
+nft-data-structs = { default-features = false, path = '../../primitives/nft' }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+pallet-evm-coder-substrate = { default-features = false, path = '../evm-coder-substrate' }
+ethereum = { default-features = false, version = "0.9.0" }
+
+[features]
+default = ["std"]
+std = [
+    "frame-support/std",
+    "frame-system/std",
+    "sp-runtime/std",
+    "sp-std/std",
+    "nft-data-structs/std",
+    "pallet-common/std",
+    "evm-coder/std",
+    "ethereum/std",
+    "pallet-evm-coder-substrate/std",
+]
+runtime-benchmarks = []
addedpallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -0,0 +1 @@
+#![cfg(feature = "runtime-benchmarking")]
addedpallets/nonfungible/src/common.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/src/common.rs
@@ -0,0 +1,228 @@
+use core::marker::PhantomData;
+
+use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
+use nft_data_structs::TokenId;
+use pallet_common::{
+	CommonCollectionOperations, CommonWeightInfo, account::CrossAccountId, with_weight,
+};
+use sp_runtime::DispatchError;
+use sp_std::vec::Vec;
+
+use crate::{
+	AccountBalance, Allowance, Config, CreateItemData, DataKind, Error, NonfungibleHandle, Owned,
+	Owner, Pallet, SelfWeightOf, TokenData, weights::WeightInfo,
+};
+
+pub struct CommonWeights<T: Config>(PhantomData<T>);
+impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+	fn create_item() -> Weight {
+		<SelfWeightOf<T>>::create_item()
+	}
+
+	fn create_multiple_items(amount: u32) -> Weight {
+		<SelfWeightOf<T>>::create_multiple_items(amount)
+	}
+
+	fn burn_item() -> Weight {
+		<SelfWeightOf<T>>::burn_item()
+	}
+
+	fn transfer() -> Weight {
+		<SelfWeightOf<T>>::transfer()
+	}
+
+	fn approve() -> Weight {
+		<SelfWeightOf<T>>::approve()
+	}
+
+	fn transfer_from() -> Weight {
+		<SelfWeightOf<T>>::transfer_from()
+	}
+
+	fn set_variable_metadata(_bytes: u32) -> Weight {
+		<SelfWeightOf<T>>::set_variable_metadata()
+	}
+}
+
+fn map_create_data<T: Config>(
+	data: nft_data_structs::CreateItemData,
+	to: &T::CrossAccountId,
+) -> Result<CreateItemData<T>, DispatchError> {
+	match data {
+		nft_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData {
+			const_data: data.const_data,
+			variable_data: data.variable_data,
+			owner: to.clone(),
+		}),
+		_ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),
+	}
+}
+
+impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {
+	fn create_item(
+		&self,
+		sender: T::CrossAccountId,
+		to: T::CrossAccountId,
+		data: nft_data_structs::CreateItemData,
+	) -> DispatchResultWithPostInfo {
+		with_weight(
+			<Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),
+			<SelfWeightOf<T>>::create_item(),
+		)
+	}
+
+	fn create_multiple_items(
+		&self,
+		sender: T::CrossAccountId,
+		to: T::CrossAccountId,
+		data: Vec<nft_data_structs::CreateItemData>,
+	) -> DispatchResultWithPostInfo {
+		let data = data
+			.into_iter()
+			.map(|d| map_create_data::<T>(d, &to))
+			.collect::<Result<Vec<_>, DispatchError>>()?;
+
+		let amount = data.len();
+		with_weight(
+			<Pallet<T>>::create_multiple_items(self, &sender, data),
+			<SelfWeightOf<T>>::create_multiple_items(amount as u32),
+		)
+	}
+
+	fn burn_item(
+		&self,
+		sender: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+		if amount == 1 {
+			with_weight(
+				<Pallet<T>>::burn(&self, &sender, token),
+				<SelfWeightOf<T>>::burn_item(),
+			)
+		} else {
+			Ok(().into())
+		}
+	}
+
+	fn transfer(
+		&self,
+		from: T::CrossAccountId,
+		to: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+		if amount == 1 {
+			with_weight(
+				<Pallet<T>>::transfer(&self, &from, &to, token),
+				<SelfWeightOf<T>>::transfer(),
+			)
+		} else {
+			Ok(().into())
+		}
+	}
+
+	fn approve(
+		&self,
+		sender: T::CrossAccountId,
+		spender: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+		with_weight(
+			if amount == 1 {
+				<Pallet<T>>::set_allowance(&self, &sender, token, Some(&spender))
+			} else {
+				<Pallet<T>>::set_allowance(&self, &sender, token, None)
+			},
+			<SelfWeightOf<T>>::approve(),
+		)
+	}
+
+	fn transfer_from(
+		&self,
+		sender: T::CrossAccountId,
+		from: T::CrossAccountId,
+		to: T::CrossAccountId,
+		token: TokenId,
+		amount: u128,
+	) -> DispatchResultWithPostInfo {
+		ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
+
+		if amount == 1 {
+			with_weight(
+				<Pallet<T>>::transfer_from(&self, &sender, &from, &to, token),
+				<SelfWeightOf<T>>::transfer_from(),
+			)
+		} else {
+			Ok(().into())
+		}
+	}
+
+	fn set_variable_metadata(
+		&self,
+		sender: T::CrossAccountId,
+		token: TokenId,
+		data: Vec<u8>,
+	) -> DispatchResultWithPostInfo {
+		with_weight(
+			<Pallet<T>>::set_variable_metadata(&self, &sender, token, data),
+			<SelfWeightOf<T>>::set_variable_metadata(),
+		)
+	}
+
+	fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {
+		<Owned<T>>::iter_prefix((self.id, account.as_sub()))
+			.map(|(id, _)| id)
+			.collect()
+	}
+
+	fn token_exists(&self, token: TokenId) -> bool {
+		<Pallet<T>>::token_exists(self, token)
+	}
+
+	fn token_owner(&self, token: TokenId) -> T::CrossAccountId {
+		<Owner<T>>::get((self.id, token))
+	}
+	fn const_metadata(&self, token: TokenId) -> Vec<u8> {
+		<TokenData<T>>::get((self.id, token, DataKind::Constant))
+	}
+	fn variable_metadata(&self, token: TokenId) -> Vec<u8> {
+		<TokenData<T>>::get((self.id, token, DataKind::Variable))
+	}
+
+	fn collection_tokens(&self) -> u32 {
+		<Pallet<T>>::total_supply(self)
+	}
+
+	fn account_balance(&self, account: T::CrossAccountId) -> u32 {
+		<AccountBalance<T>>::get((self.id, account.as_sub()))
+	}
+
+	fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {
+		if <Owner<T>>::get((self.id, token)) == account {
+			1
+		} else {
+			0
+		}
+	}
+
+	fn allowance(
+		&self,
+		sender: T::CrossAccountId,
+		spender: T::CrossAccountId,
+		token: TokenId,
+	) -> u128 {
+		if <Owner<T>>::get((self.id, token)) != sender {
+			0
+		} else if <Allowance<T>>::get((self.id, token)) == Some(spender) {
+			1
+		} else {
+			0
+		}
+	}
+}
addedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/src/erc.rs
@@ -0,0 +1,396 @@
+use core::{
+	char::{REPLACEMENT_CHARACTER, decode_utf16},
+	convert::TryInto,
+};
+use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*};
+use frame_support::BoundedVec;
+use nft_data_structs::TokenId;
+use pallet_evm_coder_substrate::dispatch_to_evm;
+use sp_core::{H160, U256};
+use sp_std::{vec::Vec, vec};
+use pallet_common::{account::CrossAccountId, erc::CommonEvmHandler};
+use pallet_evm_coder_substrate::call_internal;
+use pallet_common::erc::PrecompileOutput;
+
+use crate::{
+	AccountBalance, Config, CreateItemData, DataKind, NonfungibleHandle, Owner, Pallet, TokenData,
+	TokensMinted,
+};
+
+#[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,
+	},
+}
+
+#[derive(ToLog)]
+pub enum ERC721MintableEvents {
+	#[allow(dead_code)]
+	MintingFinished {},
+}
+
+#[solidity_interface(name = "ERC721Metadata")]
+impl<T: Config> NonfungibleHandle<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(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(&<TokenData<T>>::get((
+			self.id,
+			token_id,
+			DataKind::Constant,
+		)))
+		.into())
+	}
+}
+
+#[solidity_interface(name = "ERC721Enumerable")]
+impl<T: Config> NonfungibleHandle<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())
+	}
+
+	fn total_supply(&self) -> Result<uint256> {
+		Ok(<Pallet<T>>::total_supply(self).into())
+	}
+}
+
+#[solidity_interface(name = "ERC721", events(ERC721Events))]
+impl<T: Config> NonfungibleHandle<T> {
+	fn balance_of(&self, owner: address) -> Result<uint256> {
+		let owner = T::CrossAccountId::from_eth(owner);
+		let balance = <AccountBalance<T>>::get((self.id, owner.as_sub()));
+		Ok(balance.into())
+	}
+	fn owner_of(&self, token_id: uint256) -> Result<address> {
+		let token: TokenId = token_id.try_into()?;
+		Ok(*<Owner<T>>::get((self.id, token)).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 = token_id.try_into()?;
+
+		<Pallet<T>>::transfer_from(self, &caller, &from, &to, token)
+			.map_err(dispatch_to_evm::<T>)?;
+		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 = token_id.try_into()?;
+
+		<Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
+			.map_err(dispatch_to_evm::<T>)?;
+		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> NonfungibleHandle<T> {
+	fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let token = token_id.try_into()?;
+
+		<Pallet<T>>::burn(self, &caller, token).map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+}
+
+#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
+impl<T: Config> NonfungibleHandle<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()?;
+		if <TokensMinted<T>>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?
+			!= token_id
+		{
+			return Err("item id should be next".into());
+		}
+
+		<Pallet<T>>::create_item(
+			self,
+			&caller,
+			CreateItemData {
+				const_data: BoundedVec::default(),
+				variable_data: BoundedVec::default(),
+				owner: to,
+			},
+		)
+		.map_err(dispatch_to_evm::<T>)?;
+
+		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 <TokensMinted<T>>::get(self.id)
+			.checked_add(1)
+			.ok_or("item id overflow")?
+			!= token_id
+		{
+			return Err("item id should be next".into());
+		}
+
+		<Pallet<T>>::create_item(
+			self,
+			&caller,
+			CreateItemData {
+				const_data: Vec::<u8>::from(token_uri)
+					.try_into()
+					.map_err(|_| "token uri is too long")?,
+				variable_data: BoundedVec::default(),
+				owner: to,
+			},
+		)
+		.map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
+
+	fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
+		Err("not implementable".into())
+	}
+}
+
+#[solidity_interface(name = "ERC721UniqueExtensions")]
+impl<T: Config> NonfungibleHandle<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 = token_id.try_into()?;
+
+		<Pallet<T>>::transfer(self, &caller, &to, token).map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
+	fn next_token_id(&self) -> Result<uint256> {
+		Ok(<TokensMinted<T>>::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 = token_id.try_into()?;
+
+		<Pallet<T>>::set_variable_metadata(self, &caller, token, data)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
+	fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {
+		let token: TokenId = token_id.try_into()?;
+
+		Ok(<TokenData<T>>::get((self.id, token, DataKind::Variable)))
+	}
+
+	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 = <TokensMinted<T>>::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 {
+				const_data: BoundedVec::default(),
+				variable_data: BoundedVec::default(),
+				owner: to.clone(),
+			})
+			.collect();
+
+		<Pallet<T>>::create_multiple_items(self, &caller, 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 = <TokensMinted<T>>::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 {
+				const_data: Vec::<u8>::from(token_uri)
+					.try_into()
+					.map_err(|_| "token uri is too long")?,
+				variable_data: vec![].try_into().unwrap(),
+				owner: to.clone(),
+			});
+		}
+
+		<Pallet<T>>::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::<T>)?;
+		Ok(true)
+	}
+}
+
+#[solidity_interface(
+	name = "UniqueNFT",
+	is(
+		ERC721,
+		ERC721Metadata,
+		ERC721Enumerable,
+		ERC721UniqueExtensions,
+		ERC721Mintable,
+		ERC721Burnable,
+	)
+)]
+impl<T: Config> NonfungibleHandle<T> {}
+
+// Not a tests, but code generators
+generate_stubgen!(gen_impl, UniqueNFTCall, true);
+generate_stubgen!(gen_iface, UniqueNFTCall, false);
+
+pub const CODE: &[u8] = include_bytes!("./stubs/UniqueNFT.raw");
+
+impl<T: Config> CommonEvmHandler for NonfungibleHandle<T> {
+	const CODE: &'static [u8] = include_bytes!("./stubs/UniqueNFT.raw");
+
+	fn call(mut self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput> {
+		let result = call_internal::<UniqueNFTCall, _>(*source, &mut self, value, input);
+		self.0.recorder.evm_to_precompile_output(result)
+	}
+}
addedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
after · pallets/nonfungible/src/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]23use erc::ERC721Events;4use frame_support::{BoundedVec, ensure};5use nft_data_structs::{6	AccessMode, CUSTOM_DATA_LIMIT, Collection, CollectionId, CustomDataLimit, TokenId,7};8use pallet_common::{9	Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,10};11use sp_core::H160;12use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};13use sp_std::{vec::Vec, vec};14use core::ops::Deref;15use sp_std::collections::btree_map::BTreeMap;1617pub use pallet::*;18pub mod benchmarking;19pub mod common;20pub mod erc;21pub mod weights;2223pub struct CreateItemData<T: Config> {24	pub const_data: BoundedVec<u8, CustomDataLimit>,25	pub variable_data: BoundedVec<u8, CustomDataLimit>,26	pub owner: T::CrossAccountId,27}28pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;2930#[frame_support::pallet]31pub mod pallet {32	use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};33	use sp_std::vec::Vec;34	use nft_data_structs::{CollectionId, TokenId};35	use super::weights::WeightInfo;3637	#[pallet::error]38	pub enum Error<T> {39		/// Not Nonfungible item data used to mint in Nonfungible collection.40		NotNonfungibleDataUsedToMintFungibleCollectionToken,41		/// Used amount > 1 with NFT42		NonfungibleItemsHaveNoAmount,43	}4445	#[pallet::config]46	pub trait Config: frame_system::Config + pallet_common::Config {47		type WeightInfo: WeightInfo;48	}4950	#[pallet::pallet]51	#[pallet::generate_store(pub(super) trait Store)]52	pub struct Pallet<T>(_);5354	#[pallet::storage]55	pub(super) type TokensMinted<T: Config> =56		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;57	#[pallet::storage]58	pub(super) type TokensBurnt<T: Config> =59		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;6061	#[derive(Encode, Decode)]62	pub enum DataKind {63		Constant,64		Variable,65	}6667	#[pallet::storage]68	pub(super) type TokenData<T: Config> = StorageNMap<69		Key = (70			Key<Twox64Concat, CollectionId>,71			Key<Twox64Concat, TokenId>,72			Key<Identity, DataKind>,73		),74		Value = Vec<u8>,75		QueryKind = ValueQuery,76	>;7778	#[pallet::storage]79	pub(super) type Owner<T: Config> = StorageNMap<80		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),81		Value = T::CrossAccountId,82		QueryKind = ValueQuery,83	>;84	/// Used to enumerate tokens owned by account85	#[pallet::storage]86	pub(super) type Owned<T: Config> = StorageNMap<87		Key = (88			Key<Twox64Concat, CollectionId>,89			Key<Blake2_128Concat, T::AccountId>,90			Key<Twox64Concat, TokenId>,91		),92		Value = bool,93		QueryKind = ValueQuery,94	>;9596	#[pallet::storage]97	pub(super) type AccountBalance<T: Config> = StorageNMap<98		Key = (99			Key<Twox64Concat, CollectionId>,100			Key<Blake2_128Concat, T::AccountId>,101		),102		Value = u32,103		QueryKind = ValueQuery,104	>;105106	#[pallet::storage]107	pub(super) type Allowance<T: Config> = StorageNMap<108		Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),109		Value = T::CrossAccountId,110		QueryKind = OptionQuery,111	>;112}113114pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);115impl<T: Config> NonfungibleHandle<T> {116	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {117		Self(inner)118	}119	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {120		self.0121	}122}123impl<T: Config> Deref for NonfungibleHandle<T> {124	type Target = pallet_common::CollectionHandle<T>;125126	fn deref(&self) -> &Self::Target {127		&self.0128	}129}130131impl<T: Config> Pallet<T> {132	pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {133		<TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)134	}135	pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {136		<Owner<T>>::contains_key((collection.id, token))137	}138	pub fn ensure_owner(139		collection: &NonfungibleHandle<T>,140		token: TokenId,141		sender: &T::CrossAccountId,142	) -> DispatchResult {143		ensure!(144			&<Owner<T>>::get((collection.id, token)) == sender,145			<CommonError<T>>::NoPermission146		);147		Ok(())148	}149	pub fn item_owner(150		collection: &NonfungibleHandle<T>,151		token: TokenId,152	) -> Result<T::CrossAccountId, DispatchError> {153		let owner = <Owner<T>>::get((collection.id, token));154		ensure!(155			owner != T::CrossAccountId::default(),156			<CommonError<T>>::TokenNotFound157		);158		Ok(owner)159	}160}161162// unchecked calls skips any permission checks163impl<T: Config> Pallet<T> {164	pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {165		PalletCommon::init_collection(data)166	}167	pub fn destroy_collection(168		collection: NonfungibleHandle<T>,169		sender: &T::CrossAccountId,170	) -> DispatchResult {171		let id = collection.id;172173		// =========174175		PalletCommon::destroy_collection(collection.0, sender)?;176177		<Owner<T>>::remove_prefix((id,), None);178		<Owned<T>>::remove_prefix((id,), None);179		<TokensMinted<T>>::remove(id);180		<TokensBurnt<T>>::remove(id);181		<TokenData<T>>::remove_prefix((id,), None);182		<Allowance<T>>::remove_prefix((id,), None);183		<AccountBalance<T>>::remove_prefix((id,), None);184		Ok(())185	}186187	pub fn burn(188		collection: &NonfungibleHandle<T>,189		sender: &T::CrossAccountId,190		token: TokenId,191	) -> DispatchResult {192		let token_owner = <Pallet<T>>::item_owner(collection, token)?;193		ensure!(194			&token_owner == sender195				|| (collection.limits.owner_can_transfer196					&& collection.is_owner_or_admin(sender)?),197			<CommonError<T>>::NoPermission198		);199200		if collection.access == AccessMode::WhiteList {201			collection.check_whitelist(sender)?;202		}203204		let burnt = <TokensBurnt<T>>::get(collection.id)205			.checked_add(1)206			.ok_or(ArithmeticError::Overflow)?;207208		// =========209210		<Owner<T>>::remove((collection.id, token));211		<Owned<T>>::remove((collection.id, token_owner.as_sub(), token));212		<TokensBurnt<T>>::insert(collection.id, burnt);213		<TokenData<T>>::remove_prefix((collection.id, token), None);214		<Allowance<T>>::remove((collection.id, token));215216		collection.log_infallible(ERC721Events::Transfer {217			from: *token_owner.as_eth(),218			to: H160::default(),219			token_id: token.into(),220		});221		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(222			collection.id,223			token,224			token_owner,225			1,226		));227		return Ok(());228	}229230	pub fn transfer(231		collection: &NonfungibleHandle<T>,232		from: &T::CrossAccountId,233		to: &T::CrossAccountId,234		token: TokenId,235	) -> DispatchResult {236		ensure!(237			collection.transfers_enabled,238			<CommonError<T>>::TransferNotAllowed239		);240241		let token_owner = <Pallet<T>>::item_owner(collection, token)?;242		ensure!(243			&token_owner == from244				|| (collection.limits.owner_can_transfer && collection.is_owner_or_admin(from)?),245			<CommonError<T>>::NoPermission246		);247248		if collection.access == AccessMode::WhiteList {249			collection.check_whitelist(from)?;250			collection.check_whitelist(to)?;251		}252		<PalletCommon<T>>::ensure_correct_receiver(to)?;253254		let balance_from = <AccountBalance<T>>::get((collection.id, from.as_sub()))255			.checked_sub(1)256			.ok_or(<CommonError<T>>::TokenValueTooLow)?;257		let balance_to = if from != to {258			let balance_to = <AccountBalance<T>>::get((collection.id, to.as_sub()))259				.checked_add(1)260				.ok_or(ArithmeticError::Overflow)?;261262			ensure!(263				balance_to < collection.limits.account_token_ownership_limit(),264				<CommonError<T>>::AccountTokenLimitExceeded,265			);266267			Some(balance_to)268		} else {269			None270		};271272		collection.consume_sstores(4)?;273		collection.consume_log(3, 0)?;274275		// =========276277		if let Some(balance_to) = balance_to {278			// from != to279			if balance_from == 0 {280				<AccountBalance<T>>::remove((collection.id, from.as_sub()));281			} else {282				<AccountBalance<T>>::insert((collection.id, from.as_sub()), balance_from);283			}284			<AccountBalance<T>>::insert((collection.id, to.as_sub()), balance_to);285			<Owned<T>>::remove((collection.id, from.as_sub(), token));286			<Owned<T>>::insert((collection.id, to.as_sub(), token), true);287		}288		Self::set_allowance_unchecked(collection, from, token, None);289		<Owner<T>>::insert((collection.id, token), &to);290291		collection.log_infallible(ERC721Events::Transfer {292			from: *from.as_eth(),293			to: *to.as_eth(),294			token_id: token.into(),295		});296		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(297			collection.id,298			token,299			from.clone(),300			to.clone(),301			1,302		));303		Ok(())304	}305306	pub fn create_multiple_items(307		collection: &NonfungibleHandle<T>,308		sender: &T::CrossAccountId,309		data: Vec<CreateItemData<T>>,310	) -> DispatchResult {311		let unrestricted_minting = collection.is_owner_or_admin(sender)?;312		if !unrestricted_minting {313			ensure!(314				collection.mint_mode,315				<CommonError<T>>::PublicMintingNotAllowed316			);317			collection.check_whitelist(sender)?;318319			for item in data.iter() {320				collection.check_whitelist(&item.owner)?;321			}322		}323324		for data in data.iter() {325			<PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;326			if !data.const_data.is_empty() {327				collection.consume_sstore()?;328			}329			if !data.variable_data.is_empty() {330				collection.consume_sstore()?;331			}332			collection.consume_sstore()?;333			collection.consume_log(3, 0)?;334		}335336		let first_token = <TokensMinted<T>>::get(collection.id);337		let tokens_minted = first_token338			.checked_add(data.len() as u32)339			.ok_or(ArithmeticError::Overflow)?;340		ensure!(341			tokens_minted < collection.limits.token_limit,342			<CommonError<T>>::CollectionTokenLimitExceeded343		);344		collection.consume_sstore()?;345346		let mut balances = BTreeMap::new();347		for data in &data {348			let balance = balances349				.entry(data.owner.as_sub())350				.or_insert_with(|| <AccountBalance<T>>::get((collection.id, data.owner.as_sub())));351			*balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;352353			ensure!(354				*balance <= collection.limits.account_token_ownership_limit(),355				<CommonError<T>>::AccountTokenLimitExceeded,356			);357		}358		collection.consume_sstores(balances.len())?;359360		// =========361362		<TokensMinted<T>>::insert(collection.id, tokens_minted);363		for (account, balance) in balances {364			<AccountBalance<T>>::insert((collection.id, account), balance);365		}366		for (i, data) in data.into_iter().enumerate() {367			let token = first_token + i as u32;368369			if !data.const_data.is_empty() {370				<TokenData<T>>::insert((collection.id, token, DataKind::Constant), data.const_data);371			}372			if !data.variable_data.is_empty() {373				<TokenData<T>>::insert(374					(collection.id, token, DataKind::Variable),375					data.variable_data,376				);377			}378			<Owner<T>>::insert((collection.id, token), &data.owner);379			<Owned<T>>::insert((collection.id, data.owner.as_sub(), token), true);380381			collection.log_infallible(ERC721Events::Transfer {382				from: H160::default(),383				to: *data.owner.as_eth(),384				token_id: token.into(),385			});386		}387		Ok(())388	}389390	pub fn set_allowance_unchecked(391		collection: &NonfungibleHandle<T>,392		sender: &T::CrossAccountId,393		token: TokenId,394		spender: Option<&T::CrossAccountId>,395	) {396		if let Some(spender) = spender {397			let old_spender = <Allowance<T>>::get((collection.id, token));398			<Allowance<T>>::insert((collection.id, token), spender);399			// In ERC721 there is only one possible approved user of token, so we set400			// approved user to spender401			collection.log_infallible(ERC721Events::Approval {402				owner: *sender.as_eth(),403				approved: *spender.as_eth(),404				token_id: token.into(),405			});406			// In Unique chain, any token can have any amount of approved users, so we need to407			// set allowance of old owner to 0, and allowance of new owner to 1408			if old_spender.as_ref() != Some(spender) {409				if let Some(old_owner) = old_spender {410					<PalletCommon<T>>::deposit_event(CommonEvent::Approved(411						collection.id,412						token,413						sender.clone(),414						old_owner.clone(),415						0,416					));417				}418				<PalletCommon<T>>::deposit_event(CommonEvent::Approved(419					collection.id,420					token,421					sender.clone(),422					spender.clone(),423					1,424				));425			}426		} else {427			let old_spender = <Allowance<T>>::take((collection.id, token));428			// In ERC721 there is only one possible approved user of token, so we set429			// approved user to zero address430			collection.log_infallible(ERC721Events::Approval {431				owner: *sender.as_eth(),432				approved: H160::default(),433				token_id: token.into(),434			});435			// In Unique chain, any token can have any amount of approved users, so we need to436			// set allowance of old owner to 0437			if let Some(old_spender) = old_spender {438				<PalletCommon<T>>::deposit_event(CommonEvent::Approved(439					collection.id,440					token,441					sender.clone(),442					old_spender.clone(),443					0,444				));445			}446		}447	}448449	pub fn set_allowance(450		collection: &NonfungibleHandle<T>,451		sender: &T::CrossAccountId,452		token: TokenId,453		spender: Option<&T::CrossAccountId>,454	) -> DispatchResult {455		if collection.access == AccessMode::WhiteList {456			collection.check_whitelist(&sender)?;457			if let Some(spender) = spender {458				collection.check_whitelist(&spender)?;459			}460		}461462		if let Some(spender) = spender {463			<PalletCommon<T>>::ensure_correct_receiver(spender)?;464		}465		let token_owner = Self::item_owner(collection, token)?;466		if &token_owner != sender {467			ensure!(468				collection.ignores_owned_amount(sender)?,469				<CommonError<T>>::CantApproveMoreThanOwned470			);471		}472473		// =========474475		Self::set_allowance_unchecked(collection, sender, token, spender);476		Ok(())477	}478479	pub fn transfer_from(480		collection: &NonfungibleHandle<T>,481		spender: &T::CrossAccountId,482		from: &T::CrossAccountId,483		to: &T::CrossAccountId,484		token: TokenId,485	) -> DispatchResult {486		if spender == from {487			return Self::transfer(collection, from, to, token);488		}489		if collection.access == AccessMode::WhiteList {490			// `from`, `to` checked in [`transfer`]491			collection.check_whitelist(spender)?;492		}493494		if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {495			ensure!(496				collection.ignores_allowance(spender)?,497				<CommonError<T>>::TokenValueNotEnough498			);499		}500501		// =========502503		Self::transfer(collection, &from, to, token)?;504		// Allowance is reset in [`transfer`]505		Ok(())506	}507508	pub fn set_variable_metadata(509		collection: &NonfungibleHandle<T>,510		sender: &T::CrossAccountId,511		token: TokenId,512		data: Vec<u8>,513	) -> DispatchResult {514		ensure!(515			data.len() as u32 <= CUSTOM_DATA_LIMIT,516			<CommonError<T>>::TokenVariableDataLimitExceeded517		);518		let item_owner = Self::item_owner(collection, token)?;519		collection.check_can_update_meta(sender, &item_owner)?;520521		collection.consume_sstore()?;522523		// =========524525		<TokenData<T>>::insert((collection.id, token, DataKind::Variable), data);526		Ok(())527	}528529	/// Delegated to `create_multiple_items`530	pub fn create_item(531		collection: &NonfungibleHandle<T>,532		sender: &T::CrossAccountId,533		data: CreateItemData<T>,534	) -> DispatchResult {535		Self::create_multiple_items(collection, sender, vec![data])536	}537}
addedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

addedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -0,0 +1,326 @@
+// 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
+{}
addedpallets/nonfungible/src/weights.rsdiffbeforeafterboth
--- /dev/null
+++ b/pallets/nonfungible/src/weights.rs
@@ -0,0 +1,37 @@
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+pub trait WeightInfo {
+	fn create_item() -> Weight;
+	fn create_multiple_items(b: u32) -> Weight;
+	fn burn_item() -> Weight;
+	fn transfer() -> Weight;
+	fn approve() -> Weight;
+	fn transfer_from() -> Weight;
+	fn set_variable_metadata() -> Weight;
+}
+
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+    fn create_item() -> Weight {0}
+	fn create_multiple_items(_b: u32) -> Weight {0}
+	fn burn_item() -> Weight {0}
+	fn transfer() -> Weight {0}
+	fn approve() -> Weight {0}
+	fn transfer_from() -> Weight {0}
+	fn set_variable_metadata() -> Weight {0}
+}
+
+impl WeightInfo for () {
+    fn create_item() -> Weight {0}
+	fn create_multiple_items(_b: u32) -> Weight {0}
+	fn burn_item() -> Weight {0}
+	fn transfer() -> Weight {0}
+	fn approve() -> Weight {0}
+	fn transfer_from() -> Weight {0}
+	fn set_variable_metadata() -> Weight {0}
+}