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

difftreelog

feature(common-pallet): add changeOwner method

Grigoriy Simonov2022-08-23parent: #565dfea.patch.diff
in: master

14 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -431,6 +431,27 @@
 		};
 		Ok(mode.into())
 	}
+
+	/// Changes collection owner
+	///
+	/// @dev Owner can be changed only by current owner
+	/// @param newOwner new owner
+	fn change_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let new_owner = T::CrossAccountId::from_eth(new_owner);
+		self.change_owner_internal(caller, new_owner)
+	}
+
+impl<T: Config> CollectionHandle<T>
+where
+	T::AccountId: From<[u8; 32]>,
+{
+
+	fn change_owner_internal(&mut self, caller: T::CrossAccountId, new_owner: T::CrossAccountId) -> Result<void> {
+		self.check_is_owner(&caller).map_err(dispatch_to_evm::<T>)?;
+		self.collection.owner = new_owner.as_sub().clone();
+		save(self)
+	}
 }
 
 fn check_is_owner_or_admin<T: Config>(
@@ -440,12 +461,13 @@
 	let caller = T::CrossAccountId::from_eth(caller);
 	collection
 		.check_is_owner_or_admin(&caller)
-		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+		.map_err(dispatch_to_evm::<T>)?;
 	Ok(caller)
 }
 
 fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {
 	// TODO possibly delete for the lack of transaction
+	collection.consume_store_writes(1)?;
 	collection
 		.check_is_internal()
 		.map_err(dispatch_to_evm::<T>)?;
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -31,7 +31,103 @@
 	);
 }
 
-// Selector: 6cf113cd
+// Selector: 79cc6790
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		from;
+		amount;
+		dummy = 0;
+		return false;
+	}
+}
+
+// Selector: 942e8b22
+contract ERC20 is Dummy, ERC165, ERC20Events {
+	// 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 "";
+	}
+
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	// Selector: decimals() 313ce567
+	function decimals() public view returns (uint8) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) public returns (bool) {
+		require(false, stub_error);
+		from;
+		to;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address spender, uint256 amount) public returns (bool) {
+		require(false, stub_error);
+		spender;
+		amount;
+		dummy = 0;
+		return false;
+	}
+
+	// Selector: allowance(address,address) dd62ed3e
+	function allowance(address owner, address spender)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		spender;
+		dummy;
+		return 0;
+	}
+}
+
+// Selector: ca08c92c
 contract Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -276,105 +372,21 @@
 	//
 	// Selector: uniqueCollectionType() d34b55b8
 	function uniqueCollectionType() public returns (string memory) {
-		require(false, stub_error);
-		dummy = 0;
-		return "";
-	}
-}
-
-// Selector: 79cc6790
-contract ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) public returns (bool) {
 		require(false, stub_error);
-		from;
-		amount;
 		dummy = 0;
-		return false;
-	}
-}
-
-// Selector: 942e8b22
-contract ERC20 is Dummy, ERC165, ERC20Events {
-	// 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 "";
-	}
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: decimals() 313ce567
-	function decimals() public view returns (uint8) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) public view returns (uint256) {
-		require(false, stub_error);
-		owner;
-		dummy;
-		return 0;
 	}
 
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 amount) public returns (bool) {
-		require(false, stub_error);
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) public returns (bool) {
-		require(false, stub_error);
-		from;
-		to;
-		amount;
-		dummy = 0;
-		return false;
-	}
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address spender, uint256 amount) public returns (bool) {
+	// Changes collection owner
+	//
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
+	//
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) public {
 		require(false, stub_error);
-		spender;
-		amount;
+		newOwner;
 		dummy = 0;
-		return false;
-	}
-
-	// Selector: allowance(address,address) dd62ed3e
-	function allowance(address owner, address spender)
-		public
-		view
-		returns (uint256)
-	{
-		require(false, stub_error);
-		owner;
-		spender;
-		dummy;
-		return 0;
 	}
 }
 
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
@@ -373,7 +373,49 @@
 	}
 }
 
-// Selector: 6cf113cd
+// Selector: 780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+	// @notice Enumerate valid NFTs
+	// @param index A counter less than `totalSupply()`
+	// @return The token identifier for the `index`th NFT,
+	//  (sort order not specified)
+	//
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) public view returns (uint256) {
+		require(false, stub_error);
+		index;
+		dummy;
+		return 0;
+	}
+
+	// @dev Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		index;
+		dummy;
+		return 0;
+	}
+
+	// @notice Count NFTs tracked by this contract
+	// @return A count of valid NFTs tracked by this contract, where each one of
+	//  them has an assigned and queryable owner not equal to the zero address
+	//
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+}
+
+// Selector: ca08c92c
 contract Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -622,47 +664,17 @@
 		dummy = 0;
 		return "";
 	}
-}
 
-// Selector: 780e9d63
-contract ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid NFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
+	// Changes collection owner
 	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @dev Not implemented
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
 	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) public {
 		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @notice Count NFTs tracked by this contract
-	// @return A count of valid NFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
+		newOwner;
+		dummy = 0;
 	}
 }
 
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
@@ -371,7 +371,142 @@
 	}
 }
 
-// Selector: 6cf113cd
+// Selector: 780e9d63
+contract ERC721Enumerable is Dummy, ERC165 {
+	// @notice Enumerate valid RFTs
+	// @param index A counter less than `totalSupply()`
+	// @return The token identifier for the `index`th NFT,
+	//  (sort order not specified)
+	//
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) public view returns (uint256) {
+		require(false, stub_error);
+		index;
+		dummy;
+		return 0;
+	}
+
+	// Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		public
+		view
+		returns (uint256)
+	{
+		require(false, stub_error);
+		owner;
+		index;
+		dummy;
+		return 0;
+	}
+
+	// @notice Count RFTs tracked by this contract
+	// @return A count of valid RFTs tracked by this contract, where each one of
+	//  them has an assigned and queryable owner not equal to the zero address
+	//
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+}
+
+// Selector: 7c3bef89
+contract ERC721UniqueExtensions is Dummy, ERC165 {
+	// @notice Transfer ownership of an RFT
+	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	//  is the zero address. Throws if `tokenId` is not a valid RFT.
+	//  Throws if RFT pieces have multiple owners.
+	// @param to The new owner
+	// @param tokenId The RFT to transfer
+	// @param _value Not used for an RFT
+	//
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 tokenId) public {
+		require(false, stub_error);
+		to;
+		tokenId;
+		dummy = 0;
+	}
+
+	// @notice Burns a specific ERC721 token.
+	// @dev Throws unless `msg.sender` is the current owner or an authorized
+	//  operator for this RFT. Throws if `from` is not the current owner. Throws
+	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	//  Throws if RFT pieces have multiple owners.
+	// @param from The current owner of the RFT
+	// @param tokenId The RFT to transfer
+	// @param _value Not used for an RFT
+	//
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 tokenId) public {
+		require(false, stub_error);
+		from;
+		tokenId;
+		dummy = 0;
+	}
+
+	// @notice Returns next free RFT ID.
+	//
+	// Selector: nextTokenId() 75794a3c
+	function nextTokenId() public view returns (uint256) {
+		require(false, stub_error);
+		dummy;
+		return 0;
+	}
+
+	// @notice Function to mint multiple tokens.
+	// @dev `tokenIds` should be an array of consecutive numbers and first number
+	//  should be obtained with `nextTokenId` method
+	// @param to The new owner
+	// @param tokenIds IDs of the minted RFTs
+	//
+	// 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;
+	}
+
+	// @notice Function to mint multiple tokens with the given tokenUris.
+	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	//  numbers and first number should be obtained with `nextTokenId` method
+	// @param to The new owner
+	// @param tokens array of pairs of token ID and token URI for minted tokens
+	//
+	// 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;
+	}
+
+	// Returns EVM address for refungible token
+	//
+	// @param token ID of the token
+	//
+	// Selector: tokenContractAddress(uint256) ab76fac6
+	function tokenContractAddress(uint256 token) public view returns (address) {
+		require(false, stub_error);
+		token;
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+}
+
+// Selector: ca08c92c
 contract Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -620,140 +755,17 @@
 		dummy = 0;
 		return "";
 	}
-}
 
-// Selector: 780e9d63
-contract ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid RFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
+	// Changes collection owner
 	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) public view returns (uint256) {
-		require(false, stub_error);
-		index;
-		dummy;
-		return 0;
-	}
-
-	// Not implemented
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
 	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		public
-		view
-		returns (uint256)
-	{
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) public {
 		require(false, stub_error);
-		owner;
-		index;
-		dummy;
-		return 0;
-	}
-
-	// @notice Count RFTs tracked by this contract
-	// @return A count of valid RFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-}
-
-// Selector: 7c3bef89
-contract ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an RFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param to The new owner
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 tokenId) public {
-		require(false, stub_error);
-		to;
-		tokenId;
-		dummy = 0;
-	}
-
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the RFT
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 tokenId) public {
-		require(false, stub_error);
-		from;
-		tokenId;
-		dummy = 0;
-	}
-
-	// @notice Returns next free RFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
-	function nextTokenId() public view returns (uint256) {
-		require(false, stub_error);
-		dummy;
-		return 0;
-	}
-
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted RFTs
-	//
-	// 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;
-	}
-
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
-	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
-		public
-		returns (bool)
-	{
-		require(false, stub_error);
-		to;
-		tokens;
+		newOwner;
 		dummy = 0;
-		return false;
-	}
-
-	// Returns EVM address for refungible token
-	//
-	// @param token ID of the token
-	//
-	// Selector: tokenContractAddress(uint256) ab76fac6
-	function tokenContractAddress(uint256 token) public view returns (address) {
-		require(false, stub_error);
-		token;
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
 	}
 }
 
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -22,7 +22,50 @@
 	);
 }
 
-// Selector: 6cf113cd
+// Selector: 79cc6790
+interface ERC20UniqueExtensions is Dummy, ERC165 {
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 amount) external returns (bool);
+}
+
+// Selector: 942e8b22
+interface ERC20 is Dummy, ERC165, ERC20Events {
+	// Selector: name() 06fdde03
+	function name() external view returns (string memory);
+
+	// Selector: symbol() 95d89b41
+	function symbol() external view returns (string memory);
+
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+
+	// Selector: decimals() 313ce567
+	function decimals() external view returns (uint8);
+
+	// Selector: balanceOf(address) 70a08231
+	function balanceOf(address owner) external view returns (uint256);
+
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 amount) external returns (bool);
+
+	// Selector: transferFrom(address,address,uint256) 23b872dd
+	function transferFrom(
+		address from,
+		address to,
+		uint256 amount
+	) external returns (bool);
+
+	// Selector: approve(address,uint256) 095ea7b3
+	function approve(address spender, uint256 amount) external returns (bool);
+
+	// Selector: allowance(address,address) dd62ed3e
+	function allowance(address owner, address spender)
+		external
+		view
+		returns (uint256);
+}
+
+// Selector: ca08c92c
 interface Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -183,49 +226,14 @@
 	//
 	// Selector: uniqueCollectionType() d34b55b8
 	function uniqueCollectionType() external returns (string memory);
-}
 
-// Selector: 79cc6790
-interface ERC20UniqueExtensions is Dummy, ERC165 {
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 amount) external returns (bool);
-}
-
-// Selector: 942e8b22
-interface ERC20 is Dummy, ERC165, ERC20Events {
-	// Selector: name() 06fdde03
-	function name() external view returns (string memory);
-
-	// Selector: symbol() 95d89b41
-	function symbol() external view returns (string memory);
-
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
-
-	// Selector: decimals() 313ce567
-	function decimals() external view returns (uint8);
-
-	// Selector: balanceOf(address) 70a08231
-	function balanceOf(address owner) external view returns (uint256);
-
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 amount) external returns (bool);
-
-	// Selector: transferFrom(address,address,uint256) 23b872dd
-	function transferFrom(
-		address from,
-		address to,
-		uint256 amount
-	) external returns (bool);
-
-	// Selector: approve(address,uint256) 095ea7b3
-	function approve(address spender, uint256 amount) external returns (bool);
-
-	// Selector: allowance(address,address) dd62ed3e
-	function allowance(address owner, address spender)
-		external
-		view
-		returns (uint256);
+	// Changes collection owner
+	//
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
+	//
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) external;
 }
 
 interface UniqueFungible is
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -250,7 +250,33 @@
 	function finishMinting() external returns (bool);
 }
 
-// Selector: 6cf113cd
+// Selector: 780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	// @notice Enumerate valid NFTs
+	// @param index A counter less than `totalSupply()`
+	// @return The token identifier for the `index`th NFT,
+	//  (sort order not specified)
+	//
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	// @dev Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		external
+		view
+		returns (uint256);
+
+	// @notice Count NFTs tracked by this contract
+	// @return A count of valid NFTs tracked by this contract, where each one of
+	//  them has an assigned and queryable owner not equal to the zero address
+	//
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+}
+
+// Selector: ca08c92c
 interface Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -411,32 +437,14 @@
 	//
 	// Selector: uniqueCollectionType() d34b55b8
 	function uniqueCollectionType() external returns (string memory);
-}
 
-// Selector: 780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid NFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
+	// Changes collection owner
 	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	// @dev Not implemented
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
 	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		external
-		view
-		returns (uint256);
-
-	// @notice Count NFTs tracked by this contract
-	// @return A count of valid NFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) external;
 }
 
 // Selector: d74d154f
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -248,7 +248,96 @@
 	function finishMinting() external returns (bool);
 }
 
-// Selector: 6cf113cd
+// Selector: 780e9d63
+interface ERC721Enumerable is Dummy, ERC165 {
+	// @notice Enumerate valid RFTs
+	// @param index A counter less than `totalSupply()`
+	// @return The token identifier for the `index`th NFT,
+	//  (sort order not specified)
+	//
+	// Selector: tokenByIndex(uint256) 4f6ccce7
+	function tokenByIndex(uint256 index) external view returns (uint256);
+
+	// Not implemented
+	//
+	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
+	function tokenOfOwnerByIndex(address owner, uint256 index)
+		external
+		view
+		returns (uint256);
+
+	// @notice Count RFTs tracked by this contract
+	// @return A count of valid RFTs tracked by this contract, where each one of
+	//  them has an assigned and queryable owner not equal to the zero address
+	//
+	// Selector: totalSupply() 18160ddd
+	function totalSupply() external view returns (uint256);
+}
+
+// Selector: 7c3bef89
+interface ERC721UniqueExtensions is Dummy, ERC165 {
+	// @notice Transfer ownership of an RFT
+	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
+	//  is the zero address. Throws if `tokenId` is not a valid RFT.
+	//  Throws if RFT pieces have multiple owners.
+	// @param to The new owner
+	// @param tokenId The RFT to transfer
+	// @param _value Not used for an RFT
+	//
+	// Selector: transfer(address,uint256) a9059cbb
+	function transfer(address to, uint256 tokenId) external;
+
+	// @notice Burns a specific ERC721 token.
+	// @dev Throws unless `msg.sender` is the current owner or an authorized
+	//  operator for this RFT. Throws if `from` is not the current owner. Throws
+	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
+	//  Throws if RFT pieces have multiple owners.
+	// @param from The current owner of the RFT
+	// @param tokenId The RFT to transfer
+	// @param _value Not used for an RFT
+	//
+	// Selector: burnFrom(address,uint256) 79cc6790
+	function burnFrom(address from, uint256 tokenId) external;
+
+	// @notice Returns next free RFT ID.
+	//
+	// Selector: nextTokenId() 75794a3c
+	function nextTokenId() external view returns (uint256);
+
+	// @notice Function to mint multiple tokens.
+	// @dev `tokenIds` should be an array of consecutive numbers and first number
+	//  should be obtained with `nextTokenId` method
+	// @param to The new owner
+	// @param tokenIds IDs of the minted RFTs
+	//
+	// Selector: mintBulk(address,uint256[]) 44a9945e
+	function mintBulk(address to, uint256[] memory tokenIds)
+		external
+		returns (bool);
+
+	// @notice Function to mint multiple tokens with the given tokenUris.
+	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
+	//  numbers and first number should be obtained with `nextTokenId` method
+	// @param to The new owner
+	// @param tokens array of pairs of token ID and token URI for minted tokens
+	//
+	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
+	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
+		external
+		returns (bool);
+
+	// Returns EVM address for refungible token
+	//
+	// @param token ID of the token
+	//
+	// Selector: tokenContractAddress(uint256) ab76fac6
+	function tokenContractAddress(uint256 token)
+		external
+		view
+		returns (address);
+}
+
+// Selector: ca08c92c
 interface Collection is Dummy, ERC165 {
 	// Set collection property.
 	//
@@ -409,95 +498,14 @@
 	//
 	// Selector: uniqueCollectionType() d34b55b8
 	function uniqueCollectionType() external returns (string memory);
-}
-
-// Selector: 780e9d63
-interface ERC721Enumerable is Dummy, ERC165 {
-	// @notice Enumerate valid RFTs
-	// @param index A counter less than `totalSupply()`
-	// @return The token identifier for the `index`th NFT,
-	//  (sort order not specified)
-	//
-	// Selector: tokenByIndex(uint256) 4f6ccce7
-	function tokenByIndex(uint256 index) external view returns (uint256);
-
-	// Not implemented
-	//
-	// Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
-	function tokenOfOwnerByIndex(address owner, uint256 index)
-		external
-		view
-		returns (uint256);
-
-	// @notice Count RFTs tracked by this contract
-	// @return A count of valid RFTs tracked by this contract, where each one of
-	//  them has an assigned and queryable owner not equal to the zero address
-	//
-	// Selector: totalSupply() 18160ddd
-	function totalSupply() external view returns (uint256);
-}
 
-// Selector: 7c3bef89
-interface ERC721UniqueExtensions is Dummy, ERC165 {
-	// @notice Transfer ownership of an RFT
-	// @dev Throws unless `msg.sender` is the current owner. Throws if `to`
-	//  is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param to The new owner
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: transfer(address,uint256) a9059cbb
-	function transfer(address to, uint256 tokenId) external;
-
-	// @notice Burns a specific ERC721 token.
-	// @dev Throws unless `msg.sender` is the current owner or an authorized
-	//  operator for this RFT. Throws if `from` is not the current owner. Throws
-	//  if `to` is the zero address. Throws if `tokenId` is not a valid RFT.
-	//  Throws if RFT pieces have multiple owners.
-	// @param from The current owner of the RFT
-	// @param tokenId The RFT to transfer
-	// @param _value Not used for an RFT
-	//
-	// Selector: burnFrom(address,uint256) 79cc6790
-	function burnFrom(address from, uint256 tokenId) external;
-
-	// @notice Returns next free RFT ID.
-	//
-	// Selector: nextTokenId() 75794a3c
-	function nextTokenId() external view returns (uint256);
-
-	// @notice Function to mint multiple tokens.
-	// @dev `tokenIds` should be an array of consecutive numbers and first number
-	//  should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokenIds IDs of the minted RFTs
-	//
-	// Selector: mintBulk(address,uint256[]) 44a9945e
-	function mintBulk(address to, uint256[] memory tokenIds)
-		external
-		returns (bool);
-
-	// @notice Function to mint multiple tokens with the given tokenUris.
-	// @dev `tokenIds` is array of pairs of token ID and token URI. Token IDs should be consecutive
-	//  numbers and first number should be obtained with `nextTokenId` method
-	// @param to The new owner
-	// @param tokens array of pairs of token ID and token URI for minted tokens
+	// Changes collection owner
 	//
-	// Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
-	function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
-		external
-		returns (bool);
-
-	// Returns EVM address for refungible token
-	//
-	// @param token ID of the token
+	// @dev Owner can be changed only by current owner
+	// @param newOwner new owner
 	//
-	// Selector: tokenContractAddress(uint256) ab76fac6
-	function tokenContractAddress(uint256 token)
-		external
-		view
-		returns (address);
+	// Selector: changeOwner(address) a6f9dae1
+	function changeOwner(address newOwner) external;
 }
 
 interface UniqueRefungible is
modifiedtests/src/eth/collectionAdmin.test.tsdiffbeforeafterboth
before · tests/src/eth/collectionAdmin.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.3// Unique Network is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, either version 3 of the License, or6// (at your option) any later version.7//8// Unique Network is distributed in the hope that it will be useful,9// but WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the11// GNU General Public License for more details.1213// You should have received a copy of the GNU General Public License14// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1516import {expect} from 'chai';17import privateKey from '../substrate/privateKey';18import {19  createEthAccount,20  createEthAccountWithBalance, 21  evmCollection, 22  evmCollectionHelpers, 23  getCollectionAddressFromResult, 24  itWeb3,25} from './util/helpers';2627describe('Add collection admins', () => {28  itWeb3('Add admin by owner', async ({api, web3, privateKeyWrapper}) => {29    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);30    const collectionHelper = evmCollectionHelpers(web3, owner);31        32    const result = await collectionHelper.methods33      .createNonfungibleCollection('A', 'B', 'C')34      .send();35    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);3637    const newAdmin = await createEthAccount(web3);38    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);39    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();40    const adminList = await api.rpc.unique.adminlist(collectionId);41    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())42      .to.be.eq(newAdmin.toLocaleLowerCase());43  });4445  itWeb3('Add substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {46    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);47    const collectionHelper = evmCollectionHelpers(web3, owner);48        49    const result = await collectionHelper.methods50      .createNonfungibleCollection('A', 'B', 'C')51      .send();52    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);5354    const newAdmin = privateKeyWrapper('//Alice');55    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);56    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();5758    const adminList = await api.rpc.unique.adminlist(collectionId);59    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())60      .to.be.eq(newAdmin.address.toLocaleLowerCase());61  });62  63  itWeb3('Verify owner or admin', async ({api, web3, privateKeyWrapper}) => {64    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);65    const collectionHelper = evmCollectionHelpers(web3, owner);66        67    const result = await collectionHelper.methods68      .createNonfungibleCollection('A', 'B', 'C')69      .send();70    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);7172    const newAdmin = createEthAccount(web3);73    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);74    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newAdmin).call()).to.be.false;75    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();76    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newAdmin).call()).to.be.true;77  });7879  itWeb3('(!negative tests!) Add admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {80    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);81    const collectionHelper = evmCollectionHelpers(web3, owner);82        83    const result = await collectionHelper.methods84      .createNonfungibleCollection('A', 'B', 'C')85      .send();86    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);8788    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);89    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);90    await collectionEvm.methods.addCollectionAdmin(admin).send();91    92    const user = await createEthAccount(web3);93    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))94      .to.be.rejectedWith('NoPermission');9596    const adminList = await api.rpc.unique.adminlist(collectionId);97    expect(adminList.length).to.be.eq(1);98    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())99      .to.be.eq(admin.toLocaleLowerCase());100  });101102  itWeb3('(!negative tests!) Add admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {103    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);104    const collectionHelper = evmCollectionHelpers(web3, owner);105        106    const result = await collectionHelper.methods107      .createNonfungibleCollection('A', 'B', 'C')108      .send();109    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);110111    const notAdmin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);112    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);113    114    const user = await createEthAccount(web3);115    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))116      .to.be.rejectedWith('NoPermission');117118    const adminList = await api.rpc.unique.adminlist(collectionId);119    expect(adminList.length).to.be.eq(0);120  });121122  itWeb3('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {123    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);124    const collectionHelper = evmCollectionHelpers(web3, owner);125        126    const result = await collectionHelper.methods127      .createNonfungibleCollection('A', 'B', 'C')128      .send();129    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);130131    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);132    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);133    await collectionEvm.methods.addCollectionAdmin(admin).send();134135    const notAdmin = privateKey('//Alice');136    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin.addressRaw).call({from: admin}))137      .to.be.rejectedWith('NoPermission');138139    const adminList = await api.rpc.unique.adminlist(collectionId);140    expect(adminList.length).to.be.eq(1);141    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())142      .to.be.eq(admin.toLocaleLowerCase());143  });144  145  itWeb3('(!negative tests!) Add substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {146    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);147    const collectionHelper = evmCollectionHelpers(web3, owner);148        149    const result = await collectionHelper.methods150      .createNonfungibleCollection('A', 'B', 'C')151      .send();152    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);153154    const notAdmin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);155    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);156    const notAdmin1 = privateKey('//Alice');157    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin1.addressRaw).call({from: notAdmin0}))158      .to.be.rejectedWith('NoPermission');159160    const adminList = await api.rpc.unique.adminlist(collectionId);161    expect(adminList.length).to.be.eq(0);162  });163});164165describe('Remove collection admins', () => {166  itWeb3('Remove admin by owner', async ({api, web3, privateKeyWrapper}) => {167    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);168    const collectionHelper = evmCollectionHelpers(web3, owner);169        170    const result = await collectionHelper.methods171      .createNonfungibleCollection('A', 'B', 'C')172      .send();173    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);174175    const newAdmin = await createEthAccount(web3);176    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);177    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();178    {179      const adminList = await api.rpc.unique.adminlist(collectionId);180      expect(adminList.length).to.be.eq(1);181      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())182        .to.be.eq(newAdmin.toLocaleLowerCase());183    }184185    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();186    const adminList = await api.rpc.unique.adminlist(collectionId);187    expect(adminList.length).to.be.eq(0);188  });189190  itWeb3('Remove substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {191    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);192    const collectionHelper = evmCollectionHelpers(web3, owner);193        194    const result = await collectionHelper.methods195      .createNonfungibleCollection('A', 'B', 'C')196      .send();197    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);198199    const newAdmin = privateKeyWrapper('//Alice');200    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);201    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();202    {203      const adminList = await api.rpc.unique.adminlist(collectionId);204      expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())205        .to.be.eq(newAdmin.address.toLocaleLowerCase());206    }207    208    await collectionEvm.methods.removeCollectionAdminSubstrate(newAdmin.addressRaw).send();209    const adminList = await api.rpc.unique.adminlist(collectionId);210    expect(adminList.length).to.be.eq(0);211  });212213  itWeb3('(!negative tests!) Remove admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {214    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);215    const collectionHelper = evmCollectionHelpers(web3, owner);216        217    const result = await collectionHelper.methods218      .createNonfungibleCollection('A', 'B', 'C')219      .send();220    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);221222    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);223224    const admin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);225    await collectionEvm.methods.addCollectionAdmin(admin0).send();226    const admin1 = await createEthAccount(web3);227    await collectionEvm.methods.addCollectionAdmin(admin1).send();228229    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))230      .to.be.rejectedWith('NoPermission');231    {232      const adminList = await api.rpc.unique.adminlist(collectionId);233      expect(adminList.length).to.be.eq(2);234      expect(adminList.toString().toLocaleLowerCase())235        .to.be.deep.contains(admin0.toLocaleLowerCase())236        .to.be.deep.contains(admin1.toLocaleLowerCase());237    }238  });239240  itWeb3('(!negative tests!) Remove admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {241    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);242    const collectionHelper = evmCollectionHelpers(web3, owner);243        244    const result = await collectionHelper.methods245      .createNonfungibleCollection('A', 'B', 'C')246      .send();247    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);248249    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);250251    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);252    await collectionEvm.methods.addCollectionAdmin(admin).send();253    const notAdmin = await createEthAccount(web3);254255    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))256      .to.be.rejectedWith('NoPermission');257    {258      const adminList = await api.rpc.unique.adminlist(collectionId);259      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())260        .to.be.eq(admin.toLocaleLowerCase());261      expect(adminList.length).to.be.eq(1);262    }263  });264265  itWeb3('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {266    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);267    const collectionHelper = evmCollectionHelpers(web3, owner);268        269    const result = await collectionHelper.methods270      .createNonfungibleCollection('A', 'B', 'C')271      .send();272    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);273274    const adminSub = privateKeyWrapper('//Alice');275    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);276    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();277    const adminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);278    await collectionEvm.methods.addCollectionAdmin(adminEth).send();279280    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: adminEth}))281      .to.be.rejectedWith('NoPermission');282283    const adminList = await api.rpc.unique.adminlist(collectionId);284    expect(adminList.length).to.be.eq(2);285    expect(adminList.toString().toLocaleLowerCase())286      .to.be.deep.contains(adminSub.address.toLocaleLowerCase())287      .to.be.deep.contains(adminEth.toLocaleLowerCase());288  });289290  itWeb3('(!negative tests!) Remove substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {291    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);292    const collectionHelper = evmCollectionHelpers(web3, owner);293        294    const result = await collectionHelper.methods295      .createNonfungibleCollection('A', 'B', 'C')296      .send();297    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);298299    const adminSub = privateKeyWrapper('//Alice');300    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);301    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();302    const notAdminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);303304    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: notAdminEth}))305      .to.be.rejectedWith('NoPermission');306307    const adminList = await api.rpc.unique.adminlist(collectionId);308    expect(adminList.length).to.be.eq(1);309    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())310      .to.be.eq(adminSub.address.toLocaleLowerCase());311  });312});
after · tests/src/eth/collectionAdmin.test.ts
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.3// Unique Network is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, either version 3 of the License, or6// (at your option) any later version.7//8// Unique Network is distributed in the hope that it will be useful,9// but WITHOUT ANY WARRANTY; without even the implied warranty of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the11// GNU General Public License for more details.1213// You should have received a copy of the GNU General Public License14// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1516import {expect} from 'chai';17import privateKey from '../substrate/privateKey';18import { UNIQUE } from '../util/helpers';19import {20  createEthAccount,21  createEthAccountWithBalance, 22  evmCollection, 23  evmCollectionHelpers, 24  getCollectionAddressFromResult, 25  itWeb3,26  recordEthFee,27} from './util/helpers';2829describe('Add collection admins', () => {30  itWeb3('Add admin by owner', async ({api, web3, privateKeyWrapper}) => {31    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);32    const collectionHelper = evmCollectionHelpers(web3, owner);33        34    const result = await collectionHelper.methods35      .createNonfungibleCollection('A', 'B', 'C')36      .send();37    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);3839    const newAdmin = await createEthAccount(web3);40    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);41    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();42    const adminList = await api.rpc.unique.adminlist(collectionId);43    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())44      .to.be.eq(newAdmin.toLocaleLowerCase());45  });4647  itWeb3('Add substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {48    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);49    const collectionHelper = evmCollectionHelpers(web3, owner);50        51    const result = await collectionHelper.methods52      .createNonfungibleCollection('A', 'B', 'C')53      .send();54    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);5556    const newAdmin = privateKeyWrapper('//Alice');57    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);58    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();5960    const adminList = await api.rpc.unique.adminlist(collectionId);61    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())62      .to.be.eq(newAdmin.address.toLocaleLowerCase());63  });64  65  itWeb3('Verify owner or admin', async ({api, web3, privateKeyWrapper}) => {66    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);67    const collectionHelper = evmCollectionHelpers(web3, owner);68        69    const result = await collectionHelper.methods70      .createNonfungibleCollection('A', 'B', 'C')71      .send();72    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);7374    const newAdmin = createEthAccount(web3);75    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);76    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newAdmin).call()).to.be.false;77    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();78    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newAdmin).call()).to.be.true;79  });8081  itWeb3('(!negative tests!) Add admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {82    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);83    const collectionHelper = evmCollectionHelpers(web3, owner);84        85    const result = await collectionHelper.methods86      .createNonfungibleCollection('A', 'B', 'C')87      .send();88    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);8990    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);91    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);92    await collectionEvm.methods.addCollectionAdmin(admin).send();93    94    const user = await createEthAccount(web3);95    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: admin}))96      .to.be.rejectedWith('NoPermission');9798    const adminList = await api.rpc.unique.adminlist(collectionId);99    expect(adminList.length).to.be.eq(1);100    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())101      .to.be.eq(admin.toLocaleLowerCase());102  });103104  itWeb3('(!negative tests!) Add admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {105    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);106    const collectionHelper = evmCollectionHelpers(web3, owner);107        108    const result = await collectionHelper.methods109      .createNonfungibleCollection('A', 'B', 'C')110      .send();111    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);112113    const notAdmin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);114    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);115    116    const user = await createEthAccount(web3);117    await expect(collectionEvm.methods.addCollectionAdmin(user).call({from: notAdmin}))118      .to.be.rejectedWith('NoPermission');119120    const adminList = await api.rpc.unique.adminlist(collectionId);121    expect(adminList.length).to.be.eq(0);122  });123124  itWeb3('(!negative tests!) Add substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {125    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);126    const collectionHelper = evmCollectionHelpers(web3, owner);127        128    const result = await collectionHelper.methods129      .createNonfungibleCollection('A', 'B', 'C')130      .send();131    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);132133    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);134    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);135    await collectionEvm.methods.addCollectionAdmin(admin).send();136137    const notAdmin = privateKey('//Alice');138    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin.addressRaw).call({from: admin}))139      .to.be.rejectedWith('NoPermission');140141    const adminList = await api.rpc.unique.adminlist(collectionId);142    expect(adminList.length).to.be.eq(1);143    expect(adminList[0].asEthereum.toString().toLocaleLowerCase())144      .to.be.eq(admin.toLocaleLowerCase());145  });146  147  itWeb3('(!negative tests!) Add substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {148    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);149    const collectionHelper = evmCollectionHelpers(web3, owner);150        151    const result = await collectionHelper.methods152      .createNonfungibleCollection('A', 'B', 'C')153      .send();154    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);155156    const notAdmin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);157    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);158    const notAdmin1 = privateKey('//Alice');159    await expect(collectionEvm.methods.addCollectionAdminSubstrate(notAdmin1.addressRaw).call({from: notAdmin0}))160      .to.be.rejectedWith('NoPermission');161162    const adminList = await api.rpc.unique.adminlist(collectionId);163    expect(adminList.length).to.be.eq(0);164  });165});166167describe('Remove collection admins', () => {168  itWeb3('Remove admin by owner', async ({api, web3, privateKeyWrapper}) => {169    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);170    const collectionHelper = evmCollectionHelpers(web3, owner);171        172    const result = await collectionHelper.methods173      .createNonfungibleCollection('A', 'B', 'C')174      .send();175    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);176177    const newAdmin = await createEthAccount(web3);178    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);179    await collectionEvm.methods.addCollectionAdmin(newAdmin).send();180    {181      const adminList = await api.rpc.unique.adminlist(collectionId);182      expect(adminList.length).to.be.eq(1);183      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())184        .to.be.eq(newAdmin.toLocaleLowerCase());185    }186187    await collectionEvm.methods.removeCollectionAdmin(newAdmin).send();188    const adminList = await api.rpc.unique.adminlist(collectionId);189    expect(adminList.length).to.be.eq(0);190  });191192  itWeb3('Remove substrate admin by owner', async ({api, web3, privateKeyWrapper}) => {193    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);194    const collectionHelper = evmCollectionHelpers(web3, owner);195        196    const result = await collectionHelper.methods197      .createNonfungibleCollection('A', 'B', 'C')198      .send();199    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);200201    const newAdmin = privateKeyWrapper('//Alice');202    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);203    await collectionEvm.methods.addCollectionAdminSubstrate(newAdmin.addressRaw).send();204    {205      const adminList = await api.rpc.unique.adminlist(collectionId);206      expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())207        .to.be.eq(newAdmin.address.toLocaleLowerCase());208    }209    210    await collectionEvm.methods.removeCollectionAdminSubstrate(newAdmin.addressRaw).send();211    const adminList = await api.rpc.unique.adminlist(collectionId);212    expect(adminList.length).to.be.eq(0);213  });214215  itWeb3('(!negative tests!) Remove admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {216    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);217    const collectionHelper = evmCollectionHelpers(web3, owner);218        219    const result = await collectionHelper.methods220      .createNonfungibleCollection('A', 'B', 'C')221      .send();222    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);223224    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);225226    const admin0 = await createEthAccountWithBalance(api, web3, privateKeyWrapper);227    await collectionEvm.methods.addCollectionAdmin(admin0).send();228    const admin1 = await createEthAccount(web3);229    await collectionEvm.methods.addCollectionAdmin(admin1).send();230231    await expect(collectionEvm.methods.removeCollectionAdmin(admin1).call({from: admin0}))232      .to.be.rejectedWith('NoPermission');233    {234      const adminList = await api.rpc.unique.adminlist(collectionId);235      expect(adminList.length).to.be.eq(2);236      expect(adminList.toString().toLocaleLowerCase())237        .to.be.deep.contains(admin0.toLocaleLowerCase())238        .to.be.deep.contains(admin1.toLocaleLowerCase());239    }240  });241242  itWeb3('(!negative tests!) Remove admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {243    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);244    const collectionHelper = evmCollectionHelpers(web3, owner);245        246    const result = await collectionHelper.methods247      .createNonfungibleCollection('A', 'B', 'C')248      .send();249    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);250251    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);252253    const admin = await createEthAccountWithBalance(api, web3, privateKeyWrapper);254    await collectionEvm.methods.addCollectionAdmin(admin).send();255    const notAdmin = await createEthAccount(web3);256257    await expect(collectionEvm.methods.removeCollectionAdmin(admin).call({from: notAdmin}))258      .to.be.rejectedWith('NoPermission');259    {260      const adminList = await api.rpc.unique.adminlist(collectionId);261      expect(adminList[0].asEthereum.toString().toLocaleLowerCase())262        .to.be.eq(admin.toLocaleLowerCase());263      expect(adminList.length).to.be.eq(1);264    }265  });266267  itWeb3('(!negative tests!) Remove substrate admin by ADMIN is not allowed', async ({api, web3, privateKeyWrapper}) => {268    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);269    const collectionHelper = evmCollectionHelpers(web3, owner);270        271    const result = await collectionHelper.methods272      .createNonfungibleCollection('A', 'B', 'C')273      .send();274    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);275276    const adminSub = privateKeyWrapper('//Alice');277    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);278    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();279    const adminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);280    await collectionEvm.methods.addCollectionAdmin(adminEth).send();281282    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: adminEth}))283      .to.be.rejectedWith('NoPermission');284285    const adminList = await api.rpc.unique.adminlist(collectionId);286    expect(adminList.length).to.be.eq(2);287    expect(adminList.toString().toLocaleLowerCase())288      .to.be.deep.contains(adminSub.address.toLocaleLowerCase())289      .to.be.deep.contains(adminEth.toLocaleLowerCase());290  });291292  itWeb3('(!negative tests!) Remove substrate admin by USER is not allowed', async ({api, web3, privateKeyWrapper}) => {293    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);294    const collectionHelper = evmCollectionHelpers(web3, owner);295        296    const result = await collectionHelper.methods297      .createNonfungibleCollection('A', 'B', 'C')298      .send();299    const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);300301    const adminSub = privateKeyWrapper('//Alice');302    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);303    await collectionEvm.methods.addCollectionAdminSubstrate(adminSub.addressRaw).send();304    const notAdminEth = await createEthAccountWithBalance(api, web3, privateKeyWrapper);305306    await expect(collectionEvm.methods.removeCollectionAdminSubstrate(adminSub.addressRaw).call({from: notAdminEth}))307      .to.be.rejectedWith('NoPermission');308309    const adminList = await api.rpc.unique.adminlist(collectionId);310    expect(adminList.length).to.be.eq(1);311    expect(adminList[0].asSubstrate.toString().toLocaleLowerCase())312      .to.be.eq(adminSub.address.toLocaleLowerCase());313  });314});315316describe('Change owner tests', () => {317  itWeb3('Change owner', async ({api, web3, privateKeyWrapper}) => {318    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);319    const newOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);320    const collectionHelper = evmCollectionHelpers(web3, owner);321    const result = await collectionHelper.methods322      .createNonfungibleCollection('A', 'B', 'C')323      .send();324    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);325    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);326  327    await collectionEvm.methods.changeOwner(newOwner).send();328  329    expect(await collectionEvm.methods.verifyOwnerOrAdmin(owner).call()).to.be.false;330    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newOwner).call()).to.be.true;331  });332333  itWeb3('change owner call fee', async ({web3, api, privateKeyWrapper}) => {334    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);335    const newOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);336    const collectionHelper = evmCollectionHelpers(web3, owner);337    const result = await collectionHelper.methods338      .createNonfungibleCollection('A', 'B', 'C')339      .send();340    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);341    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);342343    const cost = await recordEthFee(api, owner, () => collectionEvm.methods.changeOwner(newOwner).send());344    expect(cost < BigInt(0.2 * Number(UNIQUE)));345    expect(cost > 0);346  });347348  itWeb3('(!negative tests!) call changeOwner by not owner', async ({api, web3, privateKeyWrapper}) => {349    const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);350    const newOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);351    const collectionHelper = evmCollectionHelpers(web3, owner);352    const result = await collectionHelper.methods353      .createNonfungibleCollection('A', 'B', 'C')354      .send();355    const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);356    const collectionEvm = evmCollection(web3, owner, collectionIdAddress);357  358    await expect(collectionEvm.methods.changeOwner(newOwner).send({from: newOwner})).to.be.rejected;359    expect(await collectionEvm.methods.verifyOwnerOrAdmin(newOwner).call()).to.be.false;360  });361});
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -116,6 +116,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -146,6 +146,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -146,6 +146,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "address", "name": "newOwner", "type": "address" }
+    ],
+    "name": "changeOwner",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],