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

difftreelog

chore code review requests

Grigoriy Simonov2022-10-12parent: #0649e61.patch.diff
in: master

20 files changed

modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -34,15 +34,14 @@
 use sp_std::vec::Vec;
 use pallet_common::{
 	erc::{
-		CommonEvmHandler, PrecompileResult, CollectionCall,
-		static_property::{key, value as property_value},
+		CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key,
+		static_property::value,
 	},
 	CollectionHandle, CollectionPropertyPermissions,
 };
 use pallet_evm::{account::CrossAccountId, PrecompileHandle};
 use pallet_evm_coder_substrate::call;
 use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
-use alloc::string::ToString;
 
 use crate::{
 	AccountBalance, Config, CreateItemData, NonfungibleHandle, Pallet, TokenData, TokensMinted,
@@ -226,37 +225,44 @@
 	/// @return token's const_metadata
 	#[solidity(rename_selector = "tokenURI")]
 	fn token_uri(&self, token_id: uint256) -> Result<string> {
+		if !self.supports_metadata() {
+			return Ok("".into());
+		}
+
 		let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
 
-		if let Ok(url) = get_token_property(self, token_id_u32, &key::url()) {
-			if !url.is_empty() {
-				return Ok(url);
+		match get_token_property(self, token_id_u32, &key::url()).as_deref() {
+			Err(_) | Ok("") => (),
+			Ok(url) => {
+				return Ok(url.into());
 			}
-		} else if !self.supports_metadata() {
-			return Err("tokenURI not set".into());
-		}
+		};
 
-		if let Some(base_uri) =
+		let base_uri =
 			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())
-		{
-			if !base_uri.is_empty() {
-				let base_uri = string::from_utf8(base_uri.into_inner()).map_err(|e| {
+				.map(BoundedVec::into_inner)
+				.map(string::from_utf8)
+				.transpose()
+				.map_err(|e| {
 					Error::Revert(alloc::format!(
 						"Can not convert value \"baseURI\" to string with error \"{}\"",
 						e
 					))
 				})?;
-				if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {
-					if !suffix.is_empty() {
-						return Ok(base_uri + suffix.as_str());
-					}
-				}
 
-				return Ok(base_uri);
+		let base_uri = match base_uri.as_deref() {
+			None | Some("") => {
+				return Ok("".into());
 			}
-		}
+			Some(base_uri) => base_uri.into(),
+		};
 
-		Ok("".into())
+		Ok(
+			match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {
+				Err(_) | Ok("") => base_uri,
+				Ok(suffix) => base_uri + suffix,
+			},
+		)
 	}
 }
 
@@ -706,17 +712,29 @@
 	}
 }
 
+impl<T: Config> NonfungibleHandle<T> {
+	pub fn supports_metadata(&self) -> bool {
+		if let Some(erc721_metadata) =
+			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
+		{
+			*erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
+		} else {
+			false
+		}
+	}
+}
+
 #[solidity_interface(
 	name = UniqueNFT,
 	is(
 		ERC721,
-		ERC721Metadata(if(this.supports_metadata())),
 		ERC721Enumerable,
 		ERC721UniqueExtensions,
 		ERC721Mintable,
 		ERC721Burnable,
 		Collection(via(common_mut returns CollectionHandle<T>)),
 		TokenProperties,
+		ERC721Metadata(if(this.supports_metadata())),
 	)
 )]
 impl<T: Config> NonfungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -297,18 +297,6 @@
 	}
 }
 
-impl<T: Config> NonfungibleHandle<T> {
-	pub fn supports_metadata(&self) -> bool {
-		if let Some(erc721_metadata) =
-			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
-		{
-			*erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
-		} else {
-			false
-		}
-	}
-}
-
 impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {
 	fn recorder(&self) -> &SubstrateRecorder<T> {
 		self.0.recorder()
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -17,6 +17,47 @@
 	}
 }
 
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+contract ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) public view returns (string memory) {
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return "";
+	}
+}
+
 /// @title A contract that allows to set and delete token properties and change token property permissions.
 /// @dev the ERC-165 identifier for this interface is 0x41369377
 contract TokenProperties is Dummy, ERC165 {
@@ -177,10 +218,10 @@
 	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
 	/// @dev EVM selector for this function is: 0x6ec0a9f1,
 	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() public view returns (Tuple17 memory) {
+	function collectionSponsor() public view returns (Tuple15 memory) {
 		require(false, stub_error);
 		dummy;
-		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+		return Tuple15(0x0000000000000000000000000000000000000000, 0);
 	}
 
 	/// Set limits for the collection.
@@ -359,10 +400,10 @@
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
-	function collectionOwner() public view returns (Tuple17 memory) {
+	function collectionOwner() public view returns (Tuple15 memory) {
 		require(false, stub_error);
 		dummy;
-		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+		return Tuple15(0x0000000000000000000000000000000000000000, 0);
 	}
 
 	/// Changes collection owner to another account
@@ -379,7 +420,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple17 {
+struct Tuple15 {
 	address field_0;
 	uint256 field_1;
 }
@@ -525,7 +566,7 @@
 	/// @param tokens array of pairs of token ID and token URI for minted tokens
 	/// @dev EVM selector for this function is: 0x36543006,
 	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {
+	function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {
 		require(false, stub_error);
 		to;
 		tokens;
@@ -535,7 +576,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple8 {
+struct Tuple6 {
 	uint256 field_0;
 	string field_1;
 }
@@ -577,48 +618,7 @@
 		require(false, stub_error);
 		dummy;
 		return 0;
-	}
-}
-
-/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-contract ERC721Metadata is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of NFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	/// @notice An abbreviated name for NFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
 	}
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) public view returns (string memory) {
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return "";
-	}
 }
 
 /// @dev inlined interface
@@ -766,11 +766,11 @@
 	Dummy,
 	ERC165,
 	ERC721,
-	ERC721Metadata,
 	ERC721Enumerable,
 	ERC721UniqueExtensions,
 	ERC721Mintable,
 	ERC721Burnable,
 	Collection,
-	TokenProperties
+	TokenProperties,
+	ERC721Metadata
 {}
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -21,19 +21,15 @@
 
 extern crate alloc;
 
-use alloc::string::ToString;
 use core::{
 	char::{REPLACEMENT_CHARACTER, decode_utf16},
 	convert::TryInto,
 };
 use evm_coder::{ToLog, execution::*, generate_stubgen, solidity, solidity_interface, types::*, weight};
-use frame_support::BoundedBTreeMap;
+use frame_support::{BoundedBTreeMap, BoundedVec};
 use pallet_common::{
 	CollectionHandle, CollectionPropertyPermissions,
-	erc::{
-		CommonEvmHandler, CollectionCall,
-		static_property::{key, value as property_value},
-	},
+	erc::{CommonEvmHandler, CollectionCall, static_property::key, static_property::value},
 };
 use pallet_evm::{account::CrossAccountId, PrecompileHandle};
 use pallet_evm_coder_substrate::{call, dispatch_to_evm};
@@ -222,37 +218,44 @@
 	/// @return token's const_metadata
 	#[solidity(rename_selector = "tokenURI")]
 	fn token_uri(&self, token_id: uint256) -> Result<string> {
+		if !self.supports_metadata() {
+			return Ok("".into());
+		}
+
 		let token_id_u32: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
 
-		if let Ok(url) = get_token_property(self, token_id_u32, &key::url()) {
-			if !url.is_empty() {
-				return Ok(url);
+		match get_token_property(self, token_id_u32, &key::url()).as_deref() {
+			Err(_) | Ok("") => (),
+			Ok(url) => {
+				return Ok(url.into());
 			}
-		} else if !self.supports_metadata() {
-			return Err("tokenURI not set".into());
-		}
+		};
 
-		if let Some(base_uri) =
+		let base_uri =
 			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::base_uri())
-		{
-			if !base_uri.is_empty() {
-				let base_uri = string::from_utf8(base_uri.into_inner()).map_err(|e| {
+				.map(BoundedVec::into_inner)
+				.map(string::from_utf8)
+				.transpose()
+				.map_err(|e| {
 					Error::Revert(alloc::format!(
 						"Can not convert value \"baseURI\" to string with error \"{}\"",
 						e
 					))
 				})?;
-				if let Ok(suffix) = get_token_property(self, token_id_u32, &key::suffix()) {
-					if !suffix.is_empty() {
-						return Ok(base_uri + suffix.as_str());
-					}
-				}
 
-				return Ok(base_uri);
+		let base_uri = match base_uri.as_deref() {
+			None | Some("") => {
+				return Ok("".into());
 			}
-		}
+			Some(base_uri) => base_uri.into(),
+		};
 
-		Ok("".into())
+		Ok(
+			match get_token_property(self, token_id_u32, &key::suffix()).as_deref() {
+				Err(_) | Ok("") => base_uri,
+				Ok(suffix) => base_uri + suffix,
+			},
+		)
 	}
 }
 
@@ -765,17 +768,29 @@
 	}
 }
 
+impl<T: Config> RefungibleHandle<T> {
+	pub fn supports_metadata(&self) -> bool {
+		if let Some(erc721_metadata) =
+			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
+		{
+			*erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
+		} else {
+			false
+		}
+	}
+}
+
 #[solidity_interface(
 	name = UniqueRefungible,
 	is(
 		ERC721,
-		ERC721Metadata(if(this.supports_metadata())),
 		ERC721Enumerable,
 		ERC721UniqueExtensions,
 		ERC721Mintable,
 		ERC721Burnable,
 		Collection(via(common_mut returns CollectionHandle<T>)),
 		TokenProperties,
+		ERC721Metadata(if(this.supports_metadata())),
 	)
 )]
 impl<T: Config> RefungibleHandle<T> where T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]> {}
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -304,18 +304,6 @@
 	}
 }
 
-impl<T: Config> RefungibleHandle<T> {
-	pub fn supports_metadata(&self) -> bool {
-		if let Some(erc721_metadata) =
-			pallet_common::Pallet::<T>::get_collection_property(self.id, &key::erc721_metadata())
-		{
-			*erc721_metadata.into_inner() == *value::ERC721_METADATA_SUPPORTED
-		} else {
-			false
-		}
-	}
-}
-
 impl<T: Config> Deref for RefungibleHandle<T> {
 	type Target = pallet_common::CollectionHandle<T>;
 
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -17,6 +17,45 @@
 	}
 }
 
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+contract ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of RFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @notice An abbreviated name for RFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() public view returns (string memory) {
+		require(false, stub_error);
+		dummy;
+		return "";
+	}
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) public view returns (string memory) {
+		require(false, stub_error);
+		tokenId;
+		dummy;
+		return "";
+	}
+}
+
 /// @title A contract that allows to set and delete token properties and change token property permissions.
 /// @dev the ERC-165 identifier for this interface is 0x41369377
 contract TokenProperties is Dummy, ERC165 {
@@ -177,10 +216,10 @@
 	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
 	/// @dev EVM selector for this function is: 0x6ec0a9f1,
 	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() public view returns (Tuple17 memory) {
+	function collectionSponsor() public view returns (Tuple15 memory) {
 		require(false, stub_error);
 		dummy;
-		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+		return Tuple15(0x0000000000000000000000000000000000000000, 0);
 	}
 
 	/// Set limits for the collection.
@@ -359,10 +398,10 @@
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
-	function collectionOwner() public view returns (Tuple17 memory) {
+	function collectionOwner() public view returns (Tuple15 memory) {
 		require(false, stub_error);
 		dummy;
-		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+		return Tuple15(0x0000000000000000000000000000000000000000, 0);
 	}
 
 	/// Changes collection owner to another account
@@ -379,7 +418,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple17 {
+struct Tuple15 {
 	address field_0;
 	uint256 field_1;
 }
@@ -527,7 +566,7 @@
 	/// @param tokens array of pairs of token ID and token URI for minted tokens
 	/// @dev EVM selector for this function is: 0x36543006,
 	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) public returns (bool) {
+	function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) public returns (bool) {
 		require(false, stub_error);
 		to;
 		tokens;
@@ -549,7 +588,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple8 {
+struct Tuple6 {
 	uint256 field_0;
 	string field_1;
 }
@@ -591,45 +630,6 @@
 		require(false, stub_error);
 		dummy;
 		return 0;
-	}
-}
-
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-contract ERC721Metadata is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of RFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	/// @notice An abbreviated name for RFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() public view returns (string memory) {
-		require(false, stub_error);
-		dummy;
-		return "";
-	}
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) public view returns (string memory) {
-		require(false, stub_error);
-		tokenId;
-		dummy;
-		return "";
 	}
 }
 
@@ -776,11 +776,11 @@
 	Dummy,
 	ERC165,
 	ERC721,
-	ERC721Metadata,
 	ERC721Enumerable,
 	ERC721UniqueExtensions,
 	ERC721Mintable,
 	ERC721Burnable,
 	Collection,
-	TokenProperties
+	TokenProperties,
+	ERC721Metadata
 {}
modifiedpallets/unique/src/eth/mod.rsdiffbeforeafterboth
--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -336,27 +336,6 @@
 	}
 
 	#[weight(<SelfWeightOf<T>>::create_collection())]
-	#[deprecated(note = "mathod was renamed to `create_rft_collection`, prefer it instead")]
-	fn create_refungible_collection(
-		&mut self,
-		caller: caller,
-		value: value,
-		name: string,
-		description: string,
-		token_prefix: string,
-	) -> Result<address> {
-		create_refungible_collection_internal::<T>(
-			caller,
-			value,
-			name,
-			description,
-			token_prefix,
-			Default::default(),
-			false,
-		)
-	}
-
-	#[weight(<SelfWeightOf<T>>::create_collection())]
 	#[solidity(rename_selector = "createERC721MetadataCompatibleRFTCollection")]
 	fn create_refungible_collection_with_properties(
 		&mut self,
modifiedpallets/unique/src/eth/stubs/CollectionHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/unique/src/eth/stubs/CollectionHelpers.soldiffbeforeafterboth
--- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol
+++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol
@@ -23,7 +23,7 @@
 }
 
 /// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x95eb98f4
+/// @dev the ERC-165 identifier for this interface is 0xd14d1221
 contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
 	/// Create an NFT collection
 	/// @param name Name of the collection
@@ -85,21 +85,6 @@
 	/// @dev EVM selector for this function is: 0xab173450,
 	///  or in textual repr: createRFTCollection(string,string,string)
 	function createRFTCollection(
-		string memory name,
-		string memory description,
-		string memory tokenPrefix
-	) public payable returns (address) {
-		require(false, stub_error);
-		name;
-		description;
-		tokenPrefix;
-		dummy = 0;
-		return 0x0000000000000000000000000000000000000000;
-	}
-
-	/// @dev EVM selector for this function is: 0x44a68ad5,
-	///  or in textual repr: createRefungibleCollection(string,string,string)
-	function createRefungibleCollection(
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
modifiedtests/src/eth/api/CollectionHelpers.soldiffbeforeafterboth
--- a/tests/src/eth/api/CollectionHelpers.sol
+++ b/tests/src/eth/api/CollectionHelpers.sol
@@ -18,7 +18,7 @@
 }
 
 /// @title Contract, which allows users to operate with collections
-/// @dev the ERC-165 identifier for this interface is 0x95eb98f4
+/// @dev the ERC-165 identifier for this interface is 0xd14d1221
 interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents {
 	/// Create an NFT collection
 	/// @param name Name of the collection
@@ -58,14 +58,6 @@
 	/// @dev EVM selector for this function is: 0xab173450,
 	///  or in textual repr: createRFTCollection(string,string,string)
 	function createRFTCollection(
-		string memory name,
-		string memory description,
-		string memory tokenPrefix
-	) external payable returns (address);
-
-	/// @dev EVM selector for this function is: 0x44a68ad5,
-	///  or in textual repr: createRefungibleCollection(string,string,string)
-	function createRefungibleCollection(
 		string memory name,
 		string memory description,
 		string memory tokenPrefix
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -12,6 +12,34 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of NFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @notice An abbreviated name for NFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
 /// @title A contract that allows to set and delete token properties and change token property permissions.
 /// @dev the ERC-165 identifier for this interface is 0x41369377
 interface TokenProperties is Dummy, ERC165 {
@@ -120,7 +148,7 @@
 	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
 	/// @dev EVM selector for this function is: 0x6ec0a9f1,
 	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() external view returns (Tuple17 memory);
+	function collectionSponsor() external view returns (Tuple15 memory);
 
 	/// Set limits for the collection.
 	/// @dev Throws error if limit not found.
@@ -237,7 +265,7 @@
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
-	function collectionOwner() external view returns (Tuple17 memory);
+	function collectionOwner() external view returns (Tuple15 memory);
 
 	/// Changes collection owner to another account
 	///
@@ -249,7 +277,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple17 {
+struct Tuple15 {
 	address field_0;
 	uint256 field_1;
 }
@@ -350,11 +378,11 @@
 	/// @param tokens array of pairs of token ID and token URI for minted tokens
 	/// @dev EVM selector for this function is: 0x36543006,
 	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);
+	function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);
 }
 
 /// @dev anonymous struct
-struct Tuple8 {
+struct Tuple6 {
 	uint256 field_0;
 	string field_1;
 }
@@ -383,35 +411,7 @@
 	///  or in textual repr: totalSupply()
 	function totalSupply() external view returns (uint256);
 }
-
-/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
-/// @dev See https://eips.ethereum.org/EIPS/eip-721
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of NFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
 
-	/// @notice An abbreviated name for NFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) external view returns (string memory);
-}
-
 /// @dev inlined interface
 interface ERC721Events {
 	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
@@ -507,11 +507,11 @@
 	Dummy,
 	ERC165,
 	ERC721,
-	ERC721Metadata,
 	ERC721Enumerable,
 	ERC721UniqueExtensions,
 	ERC721Mintable,
 	ERC721Burnable,
 	Collection,
-	TokenProperties
+	TokenProperties,
+	ERC721Metadata
 {}
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -12,6 +12,32 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
+/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
+interface ERC721Metadata is Dummy, ERC165 {
+	/// @notice A descriptive name for a collection of RFTs in this contract
+	/// @dev EVM selector for this function is: 0x06fdde03,
+	///  or in textual repr: name()
+	function name() external view returns (string memory);
+
+	/// @notice An abbreviated name for RFTs in this contract
+	/// @dev EVM selector for this function is: 0x95d89b41,
+	///  or in textual repr: symbol()
+	function symbol() external view returns (string memory);
+
+	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
+	///
+	/// @dev If the token has a `url` property and it is not empty, it is returned.
+	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
+	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
+	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
+	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
+	///
+	/// @return token's const_metadata
+	/// @dev EVM selector for this function is: 0xc87b56dd,
+	///  or in textual repr: tokenURI(uint256)
+	function tokenURI(uint256 tokenId) external view returns (string memory);
+}
+
 /// @title A contract that allows to set and delete token properties and change token property permissions.
 /// @dev the ERC-165 identifier for this interface is 0x41369377
 interface TokenProperties is Dummy, ERC165 {
@@ -120,7 +146,7 @@
 	/// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.
 	/// @dev EVM selector for this function is: 0x6ec0a9f1,
 	///  or in textual repr: collectionSponsor()
-	function collectionSponsor() external view returns (Tuple17 memory);
+	function collectionSponsor() external view returns (Tuple15 memory);
 
 	/// Set limits for the collection.
 	/// @dev Throws error if limit not found.
@@ -237,7 +263,7 @@
 	/// If address is canonical then substrate mirror is zero and vice versa.
 	/// @dev EVM selector for this function is: 0xdf727d3b,
 	///  or in textual repr: collectionOwner()
-	function collectionOwner() external view returns (Tuple17 memory);
+	function collectionOwner() external view returns (Tuple15 memory);
 
 	/// Changes collection owner to another account
 	///
@@ -249,7 +275,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple17 {
+struct Tuple15 {
 	address field_0;
 	uint256 field_1;
 }
@@ -352,7 +378,7 @@
 	/// @param tokens array of pairs of token ID and token URI for minted tokens
 	/// @dev EVM selector for this function is: 0x36543006,
 	///  or in textual repr: mintBulkWithTokenURI(address,(uint256,string)[])
-	function mintBulkWithTokenURI(address to, Tuple8[] memory tokens) external returns (bool);
+	function mintBulkWithTokenURI(address to, Tuple6[] memory tokens) external returns (bool);
 
 	/// Returns EVM address for refungible token
 	///
@@ -363,7 +389,7 @@
 }
 
 /// @dev anonymous struct
-struct Tuple8 {
+struct Tuple6 {
 	uint256 field_0;
 	string field_1;
 }
@@ -393,32 +419,6 @@
 	function totalSupply() external view returns (uint256);
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x5b5e139f
-interface ERC721Metadata is Dummy, ERC165 {
-	/// @notice A descriptive name for a collection of RFTs in this contract
-	/// @dev EVM selector for this function is: 0x06fdde03,
-	///  or in textual repr: name()
-	function name() external view returns (string memory);
-
-	/// @notice An abbreviated name for RFTs in this contract
-	/// @dev EVM selector for this function is: 0x95d89b41,
-	///  or in textual repr: symbol()
-	function symbol() external view returns (string memory);
-
-	/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
-	///
-	/// @dev If the token has a `url` property and it is not empty, it is returned.
-	///  Else If the collection does not have a property with key `schemaName` or its value is not equal to `ERC721Metadata`, it return an error `tokenURI not set`.
-	///  If the collection property `baseURI` is empty or absent, return "" (empty string)
-	///  otherwise, if token property `suffix` present and is non-empty, return concatenation of baseURI and suffix
-	///  otherwise, return concatenation of `baseURI` and stringified token id (decimal stringifying, without paddings).
-	///
-	/// @return token's const_metadata
-	/// @dev EVM selector for this function is: 0xc87b56dd,
-	///  or in textual repr: tokenURI(uint256)
-	function tokenURI(uint256 tokenId) external view returns (string memory);
-}
-
 /// @dev inlined interface
 interface ERC721Events {
 	event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
@@ -512,11 +512,11 @@
 	Dummy,
 	ERC165,
 	ERC721,
-	ERC721Metadata,
 	ERC721Enumerable,
 	ERC721UniqueExtensions,
 	ERC721Mintable,
 	ERC721Burnable,
 	Collection,
-	TokenProperties
+	TokenProperties,
+	ERC721Metadata
 {}
modifiedtests/src/eth/collectionHelpersAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/collectionHelpersAbi.json
+++ b/tests/src/eth/collectionHelpersAbi.json
@@ -84,17 +84,6 @@
   },
   {
     "inputs": [
-      { "internalType": "string", "name": "name", "type": "string" },
-      { "internalType": "string", "name": "description", "type": "string" },
-      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
-    ],
-    "name": "createRefungibleCollection",
-    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
-    "stateMutability": "payable",
-    "type": "function"
-  },
-  {
-    "inputs": [
       {
         "internalType": "address",
         "name": "collectionAddress",
modifiedtests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -1,5 +1,6 @@
 import {itEth, usingEthPlaygrounds, expect} from './util/playgrounds';
 import {IKeyringPair} from '@polkadot/types/types';
+import {Pallets} from '../util/playgrounds';
 
 describe('EVM collection properties', () => {
   let donor: IKeyringPair;
@@ -80,7 +81,7 @@
     expect(await contract.methods.supportsInterface('0x5b5e139f').call()).to.be.false;
   });
 
-  itEth('ERC721Metadata property can be set for RFT collection', async({helper}) => {
+  itEth.ifWithPallets('ERC721Metadata property can be set for RFT collection', [Pallets.ReFungible], async({helper}) => {
     const caller = await helper.eth.createAccountWithBalance(donor);
     const collection = await helper.rft.mintCollection(donor, {name: 'col', description: 'descr', tokenPrefix: 'COL'});
 
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -79,11 +79,11 @@
     });
   });
 
-  async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
+  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {
     const owner = await helper.eth.createAccountWithBalance(donor);
     const receiver = helper.eth.createAccount();
 
-    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', tokenPrefix);
+    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleNFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);
     const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
     
     const nextTokenId = await contract.methods.nextTokenId().call();
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -154,7 +154,7 @@
           { "internalType": "address", "name": "field_0", "type": "address" },
           { "internalType": "uint256", "name": "field_1", "type": "uint256" }
         ],
-        "internalType": "struct Tuple17",
+        "internalType": "struct Tuple15",
         "name": "",
         "type": "tuple"
       }
@@ -178,7 +178,7 @@
           { "internalType": "address", "name": "field_0", "type": "address" },
           { "internalType": "uint256", "name": "field_1", "type": "uint256" }
         ],
-        "internalType": "struct Tuple17",
+        "internalType": "struct Tuple15",
         "name": "",
         "type": "tuple"
       }
@@ -287,7 +287,7 @@
           { "internalType": "uint256", "name": "field_0", "type": "uint256" },
           { "internalType": "string", "name": "field_1", "type": "string" }
         ],
-        "internalType": "struct Tuple8[]",
+        "internalType": "struct Tuple6[]",
         "name": "tokens",
         "type": "tuple[]"
       }
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -154,7 +154,7 @@
           { "internalType": "address", "name": "field_0", "type": "address" },
           { "internalType": "uint256", "name": "field_1", "type": "uint256" }
         ],
-        "internalType": "struct Tuple17",
+        "internalType": "struct Tuple15",
         "name": "",
         "type": "tuple"
       }
@@ -178,7 +178,7 @@
           { "internalType": "address", "name": "field_0", "type": "address" },
           { "internalType": "uint256", "name": "field_1", "type": "uint256" }
         ],
-        "internalType": "struct Tuple17",
+        "internalType": "struct Tuple15",
         "name": "",
         "type": "tuple"
       }
@@ -287,7 +287,7 @@
           { "internalType": "uint256", "name": "field_0", "type": "uint256" },
           { "internalType": "string", "name": "field_1", "type": "string" }
         ],
-        "internalType": "struct Tuple8[]",
+        "internalType": "struct Tuple6[]",
         "name": "tokens",
         "type": "tuple[]"
       }
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
before · tests/src/eth/reFungibleToken.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';18import {EthUniqueHelper, expect, itEth, usingEthPlaygrounds} from './util/playgrounds';19import {IKeyringPair} from '@polkadot/types/types';20import {Contract} from 'web3-eth-contract';212223describe('Refungible token: Information getting', () => {24  let donor: IKeyringPair;25  let alice: IKeyringPair;2627  before(async function() {28    await usingEthPlaygrounds(async (helper, privateKey) => {29      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);3031      donor = privateKey('//Alice');32      [alice] = await helper.arrange.createAccounts([20n], donor);33    });34  });3536  itEth('totalSupply', async ({helper}) => {37    const caller = await helper.eth.createAccountWithBalance(donor);38    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});39    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});4041    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);42    const totalSupply = await contract.methods.totalSupply().call();43    expect(totalSupply).to.equal('200');44  });4546  itEth('balanceOf', async ({helper}) => {47    const caller = await helper.eth.createAccountWithBalance(donor);48    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});49    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});5051    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);52    const balance = await contract.methods.balanceOf(caller).call();53    expect(balance).to.equal('200');54  });5556  itEth('decimals', async ({helper}) => {57    const caller = await helper.eth.createAccountWithBalance(donor);58    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});59    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});6061    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);62    const decimals = await contract.methods.decimals().call();63    expect(decimals).to.equal('0');64  });65});6667// FIXME: Need erc721 for ReFubgible.68describe('Check ERC721 token URI for ReFungible', () => {69  let donor: IKeyringPair;7071  before(async function() {72    await usingEthPlaygrounds(async (helper, privateKey) => {73      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);7475      donor = privateKey('//Alice');76    });77  });7879  async function setup(helper: EthUniqueHelper, tokenPrefix: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {80    const owner = await helper.eth.createAccountWithBalance(donor);81    const receiver = helper.eth.createAccount();8283    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', tokenPrefix);84    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);85    86    const nextTokenId = await contract.methods.nextTokenId().call();87    expect(nextTokenId).to.be.equal('1');88    const result = await contract.methods.mint(89      receiver,90      nextTokenId,91    ).send();9293    if (propertyKey && propertyValue) {94      // Set URL or suffix95      await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();96    }9798    const event = result.events.Transfer;99    expect(event.address).to.be.equal(collectionAddress);100    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');101    expect(event.returnValues.to).to.be.equal(receiver);102    expect(event.returnValues.tokenId).to.be.equal(nextTokenId);103104    return {contract, nextTokenId};105  }106107  itEth('Empty tokenURI', async ({helper}) => {108    const {contract, nextTokenId} = await setup(helper, '');109    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');110  });111112  itEth('TokenURI from url', async ({helper}) => {113    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');114    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');115  });116117  itEth('TokenURI from baseURI', async ({helper}) => {118    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');119    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');120  });121122  itEth('TokenURI from baseURI + suffix', async ({helper}) => {123    const suffix = '/some/suffix';124    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);125    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);126  });127});128129describe('Refungible: Plain calls', () => {130  let donor: IKeyringPair;131  let alice: IKeyringPair;132133  before(async function() {134    await usingEthPlaygrounds(async (helper, privateKey) => {135      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);136137      donor = privateKey('//Alice');138      [alice] = await helper.arrange.createAccounts([50n], donor);139    });140  });141142  itEth('Can perform approve()', async ({helper}) => {143    const owner = await helper.eth.createAccountWithBalance(donor);144    const spender = helper.eth.createAccount();145    const collection = await helper.rft.mintCollection(alice);146    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});147148    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);149    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);150151    {152      const result = await contract.methods.approve(spender, 100).send({from: owner});153      const event = result.events.Approval;154      expect(event.address).to.be.equal(tokenAddress);155      expect(event.returnValues.owner).to.be.equal(owner);156      expect(event.returnValues.spender).to.be.equal(spender);157      expect(event.returnValues.value).to.be.equal('100');158    }159160    {161      const allowance = await contract.methods.allowance(owner, spender).call();162      expect(+allowance).to.equal(100);163    }164  });165166  itEth('Can perform transferFrom()', async ({helper}) => {167    const owner = await helper.eth.createAccountWithBalance(donor);168    const spender = await helper.eth.createAccountWithBalance(donor);169    const receiver = helper.eth.createAccount();170    const collection = await helper.rft.mintCollection(alice);171    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});172173    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);174    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);175176    await contract.methods.approve(spender, 100).send();177178    {179      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});180      let event = result.events.Transfer;181      expect(event.address).to.be.equal(tokenAddress);182      expect(event.returnValues.from).to.be.equal(owner);183      expect(event.returnValues.to).to.be.equal(receiver);184      expect(event.returnValues.value).to.be.equal('49');185186      event = result.events.Approval;187      expect(event.address).to.be.equal(tokenAddress);188      expect(event.returnValues.owner).to.be.equal(owner);189      expect(event.returnValues.spender).to.be.equal(spender);190      expect(event.returnValues.value).to.be.equal('51');191    }192193    {194      const balance = await contract.methods.balanceOf(receiver).call();195      expect(+balance).to.equal(49);196    }197198    {199      const balance = await contract.methods.balanceOf(owner).call();200      expect(+balance).to.equal(151);201    }202  });203204  itEth('Can perform transfer()', async ({helper}) => {205    const owner = await helper.eth.createAccountWithBalance(donor);206    const receiver = helper.eth.createAccount();207    const collection = await helper.rft.mintCollection(alice);208    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});209210    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);211    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);212213    {214      const result = await contract.methods.transfer(receiver, 50).send({from: owner});215      const event = result.events.Transfer;216      expect(event.address).to.be.equal(tokenAddress);217      expect(event.returnValues.from).to.be.equal(owner);218      expect(event.returnValues.to).to.be.equal(receiver);219      expect(event.returnValues.value).to.be.equal('50');220    }221222    {223      const balance = await contract.methods.balanceOf(owner).call();224      expect(+balance).to.equal(150);225    }226227    {228      const balance = await contract.methods.balanceOf(receiver).call();229      expect(+balance).to.equal(50);230    }231  });232233  itEth('Can perform repartition()', async ({helper}) => {234    const owner = await helper.eth.createAccountWithBalance(donor);235    const receiver = await helper.eth.createAccountWithBalance(donor);236    const collection = await helper.rft.mintCollection(alice);237    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});238239    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);240    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);241242    await contract.methods.repartition(200).send({from: owner});243    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);244    await contract.methods.transfer(receiver, 110).send({from: owner});245    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);246    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);247248    await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted249250    await contract.methods.transfer(receiver, 90).send({from: owner});251    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);252    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);253254    await contract.methods.repartition(150).send({from: receiver});255    await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted256    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);257  });258259  itEth('Can repartition with increased amount', async ({helper}) => {260    const owner = await helper.eth.createAccountWithBalance(donor);261    const collection = await helper.rft.mintCollection(alice);262    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});263264    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);265    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);266267    const result = await contract.methods.repartition(200).send();268269    const event = result.events.Transfer;270    expect(event.address).to.be.equal(tokenAddress);271    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');272    expect(event.returnValues.to).to.be.equal(owner);273    expect(event.returnValues.value).to.be.equal('100');274  });275276  itEth('Can repartition with decreased amount', async ({helper}) => {277    const owner = await helper.eth.createAccountWithBalance(donor);278    const collection = await helper.rft.mintCollection(alice);279    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});280281    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);282    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);283284    const result = await contract.methods.repartition(50).send();285    const event = result.events.Transfer;286    expect(event.address).to.be.equal(tokenAddress);287    expect(event.returnValues.from).to.be.equal(owner);288    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');289    expect(event.returnValues.value).to.be.equal('50');290  });291292  itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {293    const caller = await helper.eth.createAccountWithBalance(donor);294    const receiver = await helper.eth.createAccountWithBalance(donor);295    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');296    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);297298    const tokenId = await contract.methods.nextTokenId().call();299    await contract.methods.mint(caller, tokenId).send();300    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);301    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);302303    await tokenContract.methods.repartition(2).send();304    await tokenContract.methods.transfer(receiver, 1).send();305306    const events: any = [];307    contract.events.allEvents((_: any, event: any) => {308      events.push(event);309    });310    await tokenContract.methods.burnFrom(caller, 1).send();311312    const event = events[0];313    expect(event.address).to.be.equal(collectionAddress);314    expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');315    expect(event.returnValues.to).to.be.equal(receiver);316    expect(event.returnValues.tokenId).to.be.equal(tokenId);317  });318});319320describe('Refungible: Fees', () => {321  let donor: IKeyringPair;322  let alice: IKeyringPair;323324  before(async function() {325    await usingEthPlaygrounds(async (helper, privateKey) => {326      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);327328      donor = privateKey('//Alice');329      [alice] = await helper.arrange.createAccounts([50n], donor);330    });331  });332333  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {334    const owner = await helper.eth.createAccountWithBalance(donor);335    const spender = helper.eth.createAccount();336    const collection = await helper.rft.mintCollection(alice);337    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});338339    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);340    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);341342    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));343    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));344  });345346  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {347    const owner = await helper.eth.createAccountWithBalance(donor);348    const spender = await helper.eth.createAccountWithBalance(donor);349    const collection = await helper.rft.mintCollection(alice);350    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});351352    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);353    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);354355    await contract.methods.approve(spender, 100).send({from: owner});356357    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));358    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));359  });360361  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {362    const owner = await helper.eth.createAccountWithBalance(donor);363    const receiver = helper.eth.createAccount();364    const collection = await helper.rft.mintCollection(alice);365    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});366367    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);368    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);369370    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));371    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));372  });373});374375describe('Refungible: Substrate calls', () => {376  let donor: IKeyringPair;377  let alice: IKeyringPair;378379  before(async function() {380    await usingEthPlaygrounds(async (helper, privateKey) => {381      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);382383      donor = privateKey('//Alice');384      [alice] = await helper.arrange.createAccounts([50n], donor);385    });386  });387388  itEth('Events emitted for approve()', async ({helper}) => {389    const receiver = helper.eth.createAccount();390    const collection = await helper.rft.mintCollection(alice);391    const token = await collection.mintToken(alice, 200n);392393    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);394    const contract = helper.ethNativeContract.rftToken(tokenAddress);395396    const events: any = [];397    contract.events.allEvents((_: any, event: any) => {398      events.push(event);399    });400    expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;401402    const event = events[0];403    expect(event.event).to.be.equal('Approval');404    expect(event.address).to.be.equal(tokenAddress);405    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));406    expect(event.returnValues.spender).to.be.equal(receiver);407    expect(event.returnValues.value).to.be.equal('100');408  });409410  itEth('Events emitted for transferFrom()', async ({helper}) => {411    const [bob] = await helper.arrange.createAccounts([10n], donor);412    const receiver = helper.eth.createAccount();413    const collection = await helper.rft.mintCollection(alice);414    const token = await collection.mintToken(alice, 200n);415    await token.approve(alice, {Substrate: bob.address}, 100n);416417    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);418    const contract = helper.ethNativeContract.rftToken(tokenAddress);419420    const events: any = [];421    contract.events.allEvents((_: any, event: any) => {422      events.push(event);423    });424425    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver},  51n)).to.be.true;426427    let event = events[0];428    expect(event.event).to.be.equal('Transfer');429    expect(event.address).to.be.equal(tokenAddress);430    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));431    expect(event.returnValues.to).to.be.equal(receiver);432    expect(event.returnValues.value).to.be.equal('51');433434    event = events[1];435    expect(event.event).to.be.equal('Approval');436    expect(event.address).to.be.equal(tokenAddress);437    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));438    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));439    expect(event.returnValues.value).to.be.equal('49');440  });441442  itEth('Events emitted for transfer()', async ({helper}) => {443    const receiver = helper.eth.createAccount();444    const collection = await helper.rft.mintCollection(alice);445    const token = await collection.mintToken(alice, 200n);446447    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);448    const contract = helper.ethNativeContract.rftToken(tokenAddress);449450    const events: any = [];451    contract.events.allEvents((_: any, event: any) => {452      events.push(event);453    });454455    expect(await token.transfer(alice, {Ethereum: receiver},  51n)).to.be.true;456457    const event = events[0];458    expect(event.event).to.be.equal('Transfer');459    expect(event.address).to.be.equal(tokenAddress);460    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));461    expect(event.returnValues.to).to.be.equal(receiver);462    expect(event.returnValues.value).to.be.equal('51');463  });464});465466describe('ERC 1633 implementation', () => {467  let donor: IKeyringPair;468469  before(async function() {470    await usingEthPlaygrounds(async (helper, privateKey) => {471      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);472473      donor = privateKey('//Alice');474    });475  });476477  itEth('Default parent token address and id', async ({helper}) => {478    const owner = await helper.eth.createAccountWithBalance(donor);479480    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');481    const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);482    483    const tokenId = await collectionContract.methods.nextTokenId().call();484    await collectionContract.methods.mint(owner, tokenId).send();485    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);486    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);487488    expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);489    expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);490  });491});
after · tests/src/eth/reFungibleToken.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617import {Pallets, requirePalletsOrSkip} from '../util/playgrounds';18import {EthUniqueHelper, expect, itEth, usingEthPlaygrounds} from './util/playgrounds';19import {IKeyringPair} from '@polkadot/types/types';20import {Contract} from 'web3-eth-contract';212223describe('Refungible token: Information getting', () => {24  let donor: IKeyringPair;25  let alice: IKeyringPair;2627  before(async function() {28    await usingEthPlaygrounds(async (helper, privateKey) => {29      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);3031      donor = privateKey('//Alice');32      [alice] = await helper.arrange.createAccounts([20n], donor);33    });34  });3536  itEth('totalSupply', async ({helper}) => {37    const caller = await helper.eth.createAccountWithBalance(donor);38    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});39    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});4041    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);42    const totalSupply = await contract.methods.totalSupply().call();43    expect(totalSupply).to.equal('200');44  });4546  itEth('balanceOf', async ({helper}) => {47    const caller = await helper.eth.createAccountWithBalance(donor);48    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});49    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});5051    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);52    const balance = await contract.methods.balanceOf(caller).call();53    expect(balance).to.equal('200');54  });5556  itEth('decimals', async ({helper}) => {57    const caller = await helper.eth.createAccountWithBalance(donor);58    const collection = await helper.rft.mintCollection(alice, {tokenPrefix: 'MUON'});59    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: caller});6061    const contract = helper.ethNativeContract.rftTokenById(collection.collectionId, tokenId, caller);62    const decimals = await contract.methods.decimals().call();63    expect(decimals).to.equal('0');64  });65});6667// FIXME: Need erc721 for ReFubgible.68describe('Check ERC721 token URI for ReFungible', () => {69  let donor: IKeyringPair;7071  before(async function() {72    await usingEthPlaygrounds(async (helper, privateKey) => {73      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);7475      donor = privateKey('//Alice');76    });77  });7879  async function setup(helper: EthUniqueHelper, baseUri: string, propertyKey?: string, propertyValue?: string): Promise<{contract: Contract, nextTokenId: string}> {80    const owner = await helper.eth.createAccountWithBalance(donor);81    const receiver = helper.eth.createAccount();8283    const {collectionAddress} = await helper.eth.createERC721MetadataCompatibleRFTCollection(owner, 'Mint collection', 'a', 'b', baseUri);84    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);85    86    const nextTokenId = await contract.methods.nextTokenId().call();87    expect(nextTokenId).to.be.equal('1');88    const result = await contract.methods.mint(89      receiver,90      nextTokenId,91    ).send();9293    if (propertyKey && propertyValue) {94      // Set URL or suffix95      await contract.methods.setProperty(nextTokenId, propertyKey, Buffer.from(propertyValue)).send();96    }9798    const event = result.events.Transfer;99    expect(event.address).to.be.equal(collectionAddress);100    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');101    expect(event.returnValues.to).to.be.equal(receiver);102    expect(event.returnValues.tokenId).to.be.equal(nextTokenId);103104    return {contract, nextTokenId};105  }106107  itEth('Empty tokenURI', async ({helper}) => {108    const {contract, nextTokenId} = await setup(helper, '');109    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('');110  });111112  itEth('TokenURI from url', async ({helper}) => {113    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URI', 'Token URI');114    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('Token URI');115  });116117  itEth('TokenURI from baseURI', async ({helper}) => {118    const {contract, nextTokenId} = await setup(helper, 'BaseURI_');119    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_');120  });121122  itEth('TokenURI from baseURI + suffix', async ({helper}) => {123    const suffix = '/some/suffix';124    const {contract, nextTokenId} = await setup(helper, 'BaseURI_', 'URISuffix', suffix);125    expect(await contract.methods.tokenURI(nextTokenId).call()).to.be.equal('BaseURI_' + suffix);126  });127});128129describe('Refungible: Plain calls', () => {130  let donor: IKeyringPair;131  let alice: IKeyringPair;132133  before(async function() {134    await usingEthPlaygrounds(async (helper, privateKey) => {135      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);136137      donor = privateKey('//Alice');138      [alice] = await helper.arrange.createAccounts([50n], donor);139    });140  });141142  itEth('Can perform approve()', async ({helper}) => {143    const owner = await helper.eth.createAccountWithBalance(donor);144    const spender = helper.eth.createAccount();145    const collection = await helper.rft.mintCollection(alice);146    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});147148    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);149    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);150151    {152      const result = await contract.methods.approve(spender, 100).send({from: owner});153      const event = result.events.Approval;154      expect(event.address).to.be.equal(tokenAddress);155      expect(event.returnValues.owner).to.be.equal(owner);156      expect(event.returnValues.spender).to.be.equal(spender);157      expect(event.returnValues.value).to.be.equal('100');158    }159160    {161      const allowance = await contract.methods.allowance(owner, spender).call();162      expect(+allowance).to.equal(100);163    }164  });165166  itEth('Can perform transferFrom()', async ({helper}) => {167    const owner = await helper.eth.createAccountWithBalance(donor);168    const spender = await helper.eth.createAccountWithBalance(donor);169    const receiver = helper.eth.createAccount();170    const collection = await helper.rft.mintCollection(alice);171    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});172173    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);174    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);175176    await contract.methods.approve(spender, 100).send();177178    {179      const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender});180      let event = result.events.Transfer;181      expect(event.address).to.be.equal(tokenAddress);182      expect(event.returnValues.from).to.be.equal(owner);183      expect(event.returnValues.to).to.be.equal(receiver);184      expect(event.returnValues.value).to.be.equal('49');185186      event = result.events.Approval;187      expect(event.address).to.be.equal(tokenAddress);188      expect(event.returnValues.owner).to.be.equal(owner);189      expect(event.returnValues.spender).to.be.equal(spender);190      expect(event.returnValues.value).to.be.equal('51');191    }192193    {194      const balance = await contract.methods.balanceOf(receiver).call();195      expect(+balance).to.equal(49);196    }197198    {199      const balance = await contract.methods.balanceOf(owner).call();200      expect(+balance).to.equal(151);201    }202  });203204  itEth('Can perform transfer()', async ({helper}) => {205    const owner = await helper.eth.createAccountWithBalance(donor);206    const receiver = helper.eth.createAccount();207    const collection = await helper.rft.mintCollection(alice);208    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});209210    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);211    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);212213    {214      const result = await contract.methods.transfer(receiver, 50).send({from: owner});215      const event = result.events.Transfer;216      expect(event.address).to.be.equal(tokenAddress);217      expect(event.returnValues.from).to.be.equal(owner);218      expect(event.returnValues.to).to.be.equal(receiver);219      expect(event.returnValues.value).to.be.equal('50');220    }221222    {223      const balance = await contract.methods.balanceOf(owner).call();224      expect(+balance).to.equal(150);225    }226227    {228      const balance = await contract.methods.balanceOf(receiver).call();229      expect(+balance).to.equal(50);230    }231  });232233  itEth('Can perform repartition()', async ({helper}) => {234    const owner = await helper.eth.createAccountWithBalance(donor);235    const receiver = await helper.eth.createAccountWithBalance(donor);236    const collection = await helper.rft.mintCollection(alice);237    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});238239    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);240    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);241242    await contract.methods.repartition(200).send({from: owner});243    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(200);244    await contract.methods.transfer(receiver, 110).send({from: owner});245    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(90);246    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(110);247248    await expect(contract.methods.repartition(80).send({from: owner})).to.eventually.be.rejected; // Transaction is reverted249250    await contract.methods.transfer(receiver, 90).send({from: owner});251    expect(+await contract.methods.balanceOf(owner).call()).to.be.equal(0);252    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(200);253254    await contract.methods.repartition(150).send({from: receiver});255    await expect(contract.methods.transfer(owner, 160).send({from: receiver})).to.eventually.be.rejected; // Transaction is reverted256    expect(+await contract.methods.balanceOf(receiver).call()).to.be.equal(150);257  });258259  itEth('Can repartition with increased amount', async ({helper}) => {260    const owner = await helper.eth.createAccountWithBalance(donor);261    const collection = await helper.rft.mintCollection(alice);262    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});263264    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);265    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);266267    const result = await contract.methods.repartition(200).send();268269    const event = result.events.Transfer;270    expect(event.address).to.be.equal(tokenAddress);271    expect(event.returnValues.from).to.be.equal('0x0000000000000000000000000000000000000000');272    expect(event.returnValues.to).to.be.equal(owner);273    expect(event.returnValues.value).to.be.equal('100');274  });275276  itEth('Can repartition with decreased amount', async ({helper}) => {277    const owner = await helper.eth.createAccountWithBalance(donor);278    const collection = await helper.rft.mintCollection(alice);279    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});280281    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);282    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);283284    const result = await contract.methods.repartition(50).send();285    const event = result.events.Transfer;286    expect(event.address).to.be.equal(tokenAddress);287    expect(event.returnValues.from).to.be.equal(owner);288    expect(event.returnValues.to).to.be.equal('0x0000000000000000000000000000000000000000');289    expect(event.returnValues.value).to.be.equal('50');290  });291292  itEth('Receiving Transfer event on burning into full ownership', async ({helper}) => {293    const caller = await helper.eth.createAccountWithBalance(donor);294    const receiver = await helper.eth.createAccountWithBalance(donor);295    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(caller, 'Devastation', '6', '6');296    const contract = helper.ethNativeContract.collection(collectionAddress, 'rft', caller);297298    const tokenId = await contract.methods.nextTokenId().call();299    await contract.methods.mint(caller, tokenId).send();300    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);301    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, caller);302303    await tokenContract.methods.repartition(2).send();304    await tokenContract.methods.transfer(receiver, 1).send();305306    const events: any = [];307    contract.events.allEvents((_: any, event: any) => {308      events.push(event);309    });310    await tokenContract.methods.burnFrom(caller, 1).send();311312    const event = events[0];313    expect(event.address).to.be.equal(collectionAddress);314    expect(event.returnValues.from).to.be.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');315    expect(event.returnValues.to).to.be.equal(receiver);316    expect(event.returnValues.tokenId).to.be.equal(tokenId);317  });318});319320describe('Refungible: Fees', () => {321  let donor: IKeyringPair;322  let alice: IKeyringPair;323324  before(async function() {325    await usingEthPlaygrounds(async (helper, privateKey) => {326      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);327328      donor = privateKey('//Alice');329      [alice] = await helper.arrange.createAccounts([50n], donor);330    });331  });332333  itEth('approve() call fee is less than 0.2UNQ', async ({helper}) => {334    const owner = await helper.eth.createAccountWithBalance(donor);335    const spender = helper.eth.createAccount();336    const collection = await helper.rft.mintCollection(alice);337    const {tokenId} = await collection.mintToken(alice, 100n, {Ethereum: owner});338339    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);340    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);341342    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.approve(spender, 100).send({from: owner}));343    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));344  });345346  itEth('transferFrom() call fee is less than 0.2UNQ', async ({helper}) => {347    const owner = await helper.eth.createAccountWithBalance(donor);348    const spender = await helper.eth.createAccountWithBalance(donor);349    const collection = await helper.rft.mintCollection(alice);350    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});351352    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);353    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);354355    await contract.methods.approve(spender, 100).send({from: owner});356357    const cost = await helper.eth.recordCallFee(spender, () => contract.methods.transferFrom(owner, spender, 100).send({from: spender}));358    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));359  });360361  itEth('transfer() call fee is less than 0.2UNQ', async ({helper}) => {362    const owner = await helper.eth.createAccountWithBalance(donor);363    const receiver = helper.eth.createAccount();364    const collection = await helper.rft.mintCollection(alice);365    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner});366367    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);368    const contract = helper.ethNativeContract.rftToken(tokenAddress, owner);369370    const cost = await helper.eth.recordCallFee(owner, () => contract.methods.transfer(receiver, 100).send({from: owner}));371    expect(cost < BigInt(0.2 * Number(helper.balance.getOneTokenNominal())));372  });373});374375describe('Refungible: Substrate calls', () => {376  let donor: IKeyringPair;377  let alice: IKeyringPair;378379  before(async function() {380    await usingEthPlaygrounds(async (helper, privateKey) => {381      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);382383      donor = privateKey('//Alice');384      [alice] = await helper.arrange.createAccounts([50n], donor);385    });386  });387388  itEth('Events emitted for approve()', async ({helper}) => {389    const receiver = helper.eth.createAccount();390    const collection = await helper.rft.mintCollection(alice);391    const token = await collection.mintToken(alice, 200n);392393    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);394    const contract = helper.ethNativeContract.rftToken(tokenAddress);395396    const events: any = [];397    contract.events.allEvents((_: any, event: any) => {398      events.push(event);399    });400    expect(await token.approve(alice, {Ethereum: receiver}, 100n)).to.be.true;401402    const event = events[0];403    expect(event.event).to.be.equal('Approval');404    expect(event.address).to.be.equal(tokenAddress);405    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));406    expect(event.returnValues.spender).to.be.equal(receiver);407    expect(event.returnValues.value).to.be.equal('100');408  });409410  itEth('Events emitted for transferFrom()', async ({helper}) => {411    const [bob] = await helper.arrange.createAccounts([10n], donor);412    const receiver = helper.eth.createAccount();413    const collection = await helper.rft.mintCollection(alice);414    const token = await collection.mintToken(alice, 200n);415    await token.approve(alice, {Substrate: bob.address}, 100n);416417    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);418    const contract = helper.ethNativeContract.rftToken(tokenAddress);419420    const events: any = [];421    contract.events.allEvents((_: any, event: any) => {422      events.push(event);423    });424425    expect(await token.transferFrom(bob, {Substrate: alice.address}, {Ethereum: receiver},  51n)).to.be.true;426427    let event = events[0];428    expect(event.event).to.be.equal('Transfer');429    expect(event.address).to.be.equal(tokenAddress);430    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));431    expect(event.returnValues.to).to.be.equal(receiver);432    expect(event.returnValues.value).to.be.equal('51');433434    event = events[1];435    expect(event.event).to.be.equal('Approval');436    expect(event.address).to.be.equal(tokenAddress);437    expect(event.returnValues.owner).to.be.equal(helper.address.substrateToEth(alice.address));438    expect(event.returnValues.spender).to.be.equal(helper.address.substrateToEth(bob.address));439    expect(event.returnValues.value).to.be.equal('49');440  });441442  itEth('Events emitted for transfer()', async ({helper}) => {443    const receiver = helper.eth.createAccount();444    const collection = await helper.rft.mintCollection(alice);445    const token = await collection.mintToken(alice, 200n);446447    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, token.tokenId);448    const contract = helper.ethNativeContract.rftToken(tokenAddress);449450    const events: any = [];451    contract.events.allEvents((_: any, event: any) => {452      events.push(event);453    });454455    expect(await token.transfer(alice, {Ethereum: receiver},  51n)).to.be.true;456457    const event = events[0];458    expect(event.event).to.be.equal('Transfer');459    expect(event.address).to.be.equal(tokenAddress);460    expect(event.returnValues.from).to.be.equal(helper.address.substrateToEth(alice.address));461    expect(event.returnValues.to).to.be.equal(receiver);462    expect(event.returnValues.value).to.be.equal('51');463  });464});465466describe('ERC 1633 implementation', () => {467  let donor: IKeyringPair;468469  before(async function() {470    await usingEthPlaygrounds(async (helper, privateKey) => {471      requirePalletsOrSkip(this, helper, [Pallets.ReFungible]);472473      donor = privateKey('//Alice');474    });475  });476477  itEth('Default parent token address and id', async ({helper}) => {478    const owner = await helper.eth.createAccountWithBalance(donor);479480    const {collectionId, collectionAddress} = await helper.eth.createRFTCollection(owner, 'Sands', '', 'GRAIN');481    const collectionContract = helper.ethNativeContract.collection(collectionAddress, 'rft', owner);482    483    const tokenId = await collectionContract.methods.nextTokenId().call();484    await collectionContract.methods.mint(owner, tokenId).send();485    const tokenAddress = helper.ethAddress.fromTokenId(collectionId, tokenId);486    const tokenContract = helper.ethNativeContract.rftToken(tokenAddress, owner);487488    expect(await tokenContract.methods.parentToken().call()).to.be.equal(collectionAddress);489    expect(await tokenContract.methods.parentTokenId().call()).to.be.equal(tokenId);490  });491});