git.delta.rocks / unique-network / refs/commits / 5c9ffb7fd329

difftreelog

Merge pull request #827 from UniqueNetwork/fix/frontier-get-code

Yaroslav Bolyukin2023-01-16parents: #316164b #706a257.patch.diff
in: master
Fix eth_getCode

15 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2467,7 +2467,7 @@
 [[package]]
 name = "fc-consensus"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "async-trait",
  "fc-db",
@@ -2486,7 +2486,7 @@
 [[package]]
 name = "fc-db"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "fp-storage",
  "kvdb-rocksdb",
@@ -2505,7 +2505,7 @@
 [[package]]
 name = "fc-mapping-sync"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "fc-db",
  "fp-consensus",
@@ -2522,7 +2522,7 @@
 [[package]]
 name = "fc-rpc"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "ethereum-types 0.14.1",
@@ -2565,7 +2565,7 @@
 [[package]]
 name = "fc-rpc-core"
 version = "1.1.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "ethereum-types 0.14.1",
@@ -2730,7 +2730,7 @@
 [[package]]
 name = "fp-consensus"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "parity-scale-codec 3.2.1",
@@ -2742,7 +2742,7 @@
 [[package]]
 name = "fp-ethereum"
 version = "1.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "ethereum-types 0.14.1",
@@ -2757,7 +2757,7 @@
 [[package]]
 name = "fp-evm"
 version = "3.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "evm",
  "frame-support",
@@ -2771,7 +2771,7 @@
 [[package]]
 name = "fp-evm-mapping"
 version = "0.1.0"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "frame-support",
  "sp-core",
@@ -2780,7 +2780,7 @@
 [[package]]
 name = "fp-rpc"
 version = "3.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "ethereum-types 0.14.1",
@@ -2797,7 +2797,7 @@
 [[package]]
 name = "fp-self-contained"
 version = "1.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "frame-support",
@@ -2810,7 +2810,7 @@
 [[package]]
 name = "fp-storage"
 version = "2.0.0"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "parity-scale-codec 3.2.1",
  "serde",
@@ -5687,7 +5687,7 @@
 [[package]]
 name = "pallet-base-fee"
 version = "1.0.0"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "fp-evm",
  "frame-support",
@@ -5952,7 +5952,7 @@
 [[package]]
 name = "pallet-ethereum"
 version = "4.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "ethereum 0.14.0",
  "ethereum-types 0.14.1",
@@ -5980,7 +5980,7 @@
 [[package]]
 name = "pallet-evm"
 version = "6.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "environmental",
  "evm",
@@ -6066,7 +6066,7 @@
 [[package]]
 name = "pallet-evm-precompile-simple"
 version = "2.0.0-dev"
-source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#cf1894629c7df1c4dafe58aa773627a3d940da14"
+source = "git+https://github.com/uniquenetwork/frontier?branch=unique-polkadot-v0.9.36#ef8d2809ed05da0664e9b43cb87436451295b634"
 dependencies = [
  "fp-evm",
  "ripemd",
modifiedcrates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -811,6 +811,13 @@
 	let list = generics_list(gen);
 	quote! { <#list> }
 }
+fn generics_stub(gen: &Generics) -> proc_macro2::TokenStream {
+	if gen.params.is_empty() {
+		return quote! {};
+	}
+	let params = (0..gen.params.len()).map(|_| quote! {()});
+	quote! {<#(#params,)*>}
+}
 fn generics_data(gen: &Generics) -> proc_macro2::TokenStream {
 	let list = generics_list(gen);
 	if gen.params.len() == 1 {
@@ -866,6 +873,7 @@
 		let generics = self.generics;
 		let gen_ref = generics_reference(&generics);
 		let gen_data = generics_data(&generics);
+		let gen_stub = generics_stub(&generics);
 		let gen_where = &generics.where_clause;
 
 		let call_sub = self
@@ -936,6 +944,12 @@
 		let solidity_events_idents = self.info.events.0.iter().map(|is| is.name.clone());
 		let docs = &self.docs;
 
+		let expect_selector = self.info.expect_selector.map(|s| {
+            quote! {
+                const _: () = assert!(#s == u32::from_be_bytes(<#call_name #gen_stub>::interface_id()), "selector mismatch, review contained function selectors");
+            }
+        });
+
 		quote! {
 			#(
 				const _: ::core::marker::PhantomData<#solidity_events_idents> = ::core::marker::PhantomData;
@@ -952,12 +966,15 @@
 					#call_sub,
 				)*
 			}
+
+			#expect_selector
+
 			impl #gen_ref #call_name #gen_ref {
 				#(
 					#consts
 				)*
 				/// Return this call ERC165 selector
-				pub fn interface_id() -> ::evm_coder::types::bytes4 {
+				pub const fn interface_id() -> ::evm_coder::types::bytes4 {
 					let mut interface_id = 0;
 					#(#interface_id)*
 					#(#inline_interface_id)*
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -524,11 +524,6 @@
 
 		Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))
 	}
-
-	/// @notice Returns collection helper contract address
-	fn collection_helper_address(&self) -> Result<address> {
-		Ok(T::ContractAddress::get())
-	}
 }
 
 /// @title ERC721 Token that can be irreversibly burned (destroyed).
@@ -1058,6 +1053,11 @@
 
 		Ok(token_id.into())
 	}
+
+	/// @notice Returns collection helper contract address
+	fn collection_helper_address(&self) -> Result<address> {
+		Ok(T::ContractAddress::get())
+	}
 }
 
 #[solidity_interface(
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
@@ -785,7 +785,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4
+/// @dev the ERC-165 identifier for this interface is 0x16de3152
 contract ERC721UniqueExtensions is Dummy, ERC165 {
 	/// @notice A descriptive name for a collection of NFTs in this contract
 	/// @dev EVM selector for this function is: 0x06fdde03,
@@ -986,6 +986,15 @@
 		dummy = 0;
 		return 0;
 	}
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
 }
 
 /// @dev anonymous struct
@@ -1043,7 +1052,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x983a942b
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
 contract ERC721 is Dummy, ERC165, ERC721Events {
 	/// @notice Count all NFTs assigned to an owner
 	/// @dev NFTs assigned to the zero address are considered invalid, and this
@@ -1175,15 +1184,6 @@
 		operator;
 		dummy;
 		return false;
-	}
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() public view returns (address) {
-		require(false, stub_error);
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
 	}
 }
 
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -295,7 +295,9 @@
 	MintingFinished {},
 }
 
-#[solidity_interface(name = ERC721Metadata)]
+/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
+/// @dev See https://eips.ethereum.org/EIPS/eip-721
+#[solidity_interface(name = ERC721Metadata, expect_selector = 0x5b5e139f)]
 impl<T: Config> RefungibleHandle<T>
 where
 	T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,
@@ -364,7 +366,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
 /// @dev See https://eips.ethereum.org/EIPS/eip-721
-#[solidity_interface(name = ERC721Enumerable)]
+#[solidity_interface(name = ERC721Enumerable, expect_selector = 0x780e9d63)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Enumerate valid RFTs
 	/// @param index A counter less than `totalSupply()`
@@ -391,7 +393,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-#[solidity_interface(name = ERC721, events(ERC721Events))]
+#[solidity_interface(name = ERC721, events(ERC721Events), expect_selector = 0x80ac58cd)]
 impl<T: Config> RefungibleHandle<T> {
 	/// @notice Count all RFTs assigned to an owner
 	/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -422,6 +424,7 @@
 	}
 
 	/// @dev Not implemented
+	#[solidity(rename_selector = "safeTransferFrom")]
 	fn safe_transfer_from_with_data(
 		&mut self,
 		_from: address,
@@ -434,6 +437,7 @@
 	}
 
 	/// @dev Not implemented
+	#[solidity(rename_selector = "safeTransferFrom")]
 	fn safe_transfer_from(
 		&mut self,
 		_from: address,
@@ -517,11 +521,6 @@
 
 		Ok(<Pallet<T>>::allowance_for_all(self, &owner, &operator))
 	}
-
-	/// @notice Returns collection helper contract address
-	fn collection_helper_address(&self) -> Result<address> {
-		Ok(T::ContractAddress::get())
-	}
 }
 
 /// Returns amount of pieces of `token` that `owner` have
@@ -1116,6 +1115,11 @@
 			token.try_into().map_err(|_| "token id overflow")?,
 		))
 	}
+
+	/// @notice Returns collection helper contract address
+	fn collection_helper_address(&self) -> Result<address> {
+		Ok(T::ContractAddress::get())
+	}
 }
 
 #[solidity_interface(
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
@@ -641,6 +641,8 @@
 	TransferEnabled
 }
 
+/// @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
@@ -783,7 +785,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xabf30dc2
+/// @dev the ERC-165 identifier for this interface is 0xb365c124
 contract ERC721UniqueExtensions is Dummy, ERC165 {
 	/// @notice A descriptive name for a collection of NFTs in this contract
 	/// @dev EVM selector for this function is: 0x06fdde03,
@@ -985,6 +987,15 @@
 		dummy;
 		return 0x0000000000000000000000000000000000000000;
 	}
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
 }
 
 /// @dev anonymous struct
@@ -1042,7 +1053,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x4016cd87
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
 contract ERC721 is Dummy, ERC165, ERC721Events {
 	/// @notice Count all RFTs assigned to an owner
 	/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -1075,9 +1086,9 @@
 	}
 
 	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x60a11672,
-	///  or in textual repr: safeTransferFromWithData(address,address,uint256,bytes)
-	function safeTransferFromWithData(
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
 		address from,
 		address to,
 		uint256 tokenId,
@@ -1172,15 +1183,6 @@
 		operator;
 		dummy;
 		return false;
-	}
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() public view returns (address) {
-		require(false, stub_error);
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
 	}
 }
 
modifiedruntime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth
--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -139,8 +139,7 @@
 			| BalanceOf { .. }
 			| OwnerOf { .. }
 			| GetApproved { .. }
-			| IsApprovedForAll { .. }
-			| CollectionHelperAddress => None,
+			| IsApprovedForAll { .. } => None,
 
 			// Not sponsored
 			SafeTransferFromWithData { .. }
@@ -230,7 +229,8 @@
 			| CrossOwnerOf { .. }
 			| Properties { .. }
 			| NextTokenId
-			| TokenContractAddress { .. } => None,
+			| TokenContractAddress { .. }
+			| CollectionHelperAddress => None,
 
 			// Not sponsored
 			BurnFrom { .. }
modifiedruntime/common/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -451,7 +451,9 @@
                 }
 
                 fn account_code_at(address: H160) -> Vec<u8> {
-                    EVM::account_codes(address)
+                    use pallet_evm::OnMethodCall;
+                    <Runtime as pallet_evm::Config>::OnMethodCall::get_code(&address)
+                        .unwrap_or_else(|| EVM::account_codes(address))
                 }
 
                 fn author() -> H160 {
modifiedtests/src/eth/abi/nonFungible.jsondiffbeforeafterboth
before · tests/src/eth/abi/nonFungible.json
1[2  {3    "anonymous": false,4    "inputs": [5      {6        "indexed": true,7        "internalType": "address",8        "name": "owner",9        "type": "address"10      },11      {12        "indexed": true,13        "internalType": "address",14        "name": "approved",15        "type": "address"16      },17      {18        "indexed": true,19        "internalType": "uint256",20        "name": "tokenId",21        "type": "uint256"22      }23    ],24    "name": "Approval",25    "type": "event"26  },27  {28    "anonymous": false,29    "inputs": [30      {31        "indexed": true,32        "internalType": "address",33        "name": "owner",34        "type": "address"35      },36      {37        "indexed": true,38        "internalType": "address",39        "name": "operator",40        "type": "address"41      },42      {43        "indexed": false,44        "internalType": "bool",45        "name": "approved",46        "type": "bool"47      }48    ],49    "name": "ApprovalForAll",50    "type": "event"51  },52  {53    "anonymous": false,54    "inputs": [55      {56        "indexed": true,57        "internalType": "address",58        "name": "from",59        "type": "address"60      },61      {62        "indexed": true,63        "internalType": "address",64        "name": "to",65        "type": "address"66      },67      {68        "indexed": true,69        "internalType": "uint256",70        "name": "tokenId",71        "type": "uint256"72      }73    ],74    "name": "Transfer",75    "type": "event"76  },77  {78    "inputs": [79      {80        "components": [81          { "internalType": "address", "name": "eth", "type": "address" },82          { "internalType": "uint256", "name": "sub", "type": "uint256" }83        ],84        "internalType": "struct CrossAddress",85        "name": "newAdmin",86        "type": "tuple"87      }88    ],89    "name": "addCollectionAdminCross",90    "outputs": [],91    "stateMutability": "nonpayable",92    "type": "function"93  },94  {95    "inputs": [96      {97        "components": [98          { "internalType": "address", "name": "eth", "type": "address" },99          { "internalType": "uint256", "name": "sub", "type": "uint256" }100        ],101        "internalType": "struct CrossAddress",102        "name": "user",103        "type": "tuple"104      }105    ],106    "name": "addToCollectionAllowListCross",107    "outputs": [],108    "stateMutability": "nonpayable",109    "type": "function"110  },111  {112    "inputs": [113      {114        "components": [115          { "internalType": "address", "name": "eth", "type": "address" },116          { "internalType": "uint256", "name": "sub", "type": "uint256" }117        ],118        "internalType": "struct CrossAddress",119        "name": "user",120        "type": "tuple"121      }122    ],123    "name": "allowlistedCross",124    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],125    "stateMutability": "view",126    "type": "function"127  },128  {129    "inputs": [130      { "internalType": "address", "name": "approved", "type": "address" },131      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }132    ],133    "name": "approve",134    "outputs": [],135    "stateMutability": "nonpayable",136    "type": "function"137  },138  {139    "inputs": [140      {141        "components": [142          { "internalType": "address", "name": "eth", "type": "address" },143          { "internalType": "uint256", "name": "sub", "type": "uint256" }144        ],145        "internalType": "struct CrossAddress",146        "name": "approved",147        "type": "tuple"148      },149      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }150    ],151    "name": "approveCross",152    "outputs": [],153    "stateMutability": "nonpayable",154    "type": "function"155  },156  {157    "inputs": [158      { "internalType": "address", "name": "owner", "type": "address" }159    ],160    "name": "balanceOf",161    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],162    "stateMutability": "view",163    "type": "function"164  },165  {166    "inputs": [167      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }168    ],169    "name": "burn",170    "outputs": [],171    "stateMutability": "nonpayable",172    "type": "function"173  },174  {175    "inputs": [176      {177        "components": [178          { "internalType": "address", "name": "eth", "type": "address" },179          { "internalType": "uint256", "name": "sub", "type": "uint256" }180        ],181        "internalType": "struct CrossAddress",182        "name": "from",183        "type": "tuple"184      },185      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }186    ],187    "name": "burnFromCross",188    "outputs": [],189    "stateMutability": "nonpayable",190    "type": "function"191  },192  {193    "inputs": [194      {195        "components": [196          { "internalType": "address", "name": "eth", "type": "address" },197          { "internalType": "uint256", "name": "sub", "type": "uint256" }198        ],199        "internalType": "struct CrossAddress",200        "name": "newOwner",201        "type": "tuple"202      }203    ],204    "name": "changeCollectionOwnerCross",205    "outputs": [],206    "stateMutability": "nonpayable",207    "type": "function"208  },209  {210    "inputs": [],211    "name": "collectionAdmins",212    "outputs": [213      {214        "components": [215          { "internalType": "address", "name": "eth", "type": "address" },216          { "internalType": "uint256", "name": "sub", "type": "uint256" }217        ],218        "internalType": "struct CrossAddress[]",219        "name": "",220        "type": "tuple[]"221      }222    ],223    "stateMutability": "view",224    "type": "function"225  },226  {227    "inputs": [],228    "name": "collectionHelperAddress",229    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],230    "stateMutability": "view",231    "type": "function"232  },233  {234    "inputs": [],235    "name": "collectionLimits",236    "outputs": [237      {238        "components": [239          {240            "internalType": "enum CollectionLimitField",241            "name": "field",242            "type": "uint8"243          },244          {245            "components": [246              { "internalType": "bool", "name": "status", "type": "bool" },247              { "internalType": "uint256", "name": "value", "type": "uint256" }248            ],249            "internalType": "struct OptionUint256",250            "name": "value",251            "type": "tuple"252          }253        ],254        "internalType": "struct CollectionLimit[]",255        "name": "",256        "type": "tuple[]"257      }258    ],259    "stateMutability": "view",260    "type": "function"261  },262  {263    "inputs": [],264    "name": "collectionNestingPermissions",265    "outputs": [266      {267        "components": [268          {269            "internalType": "enum CollectionPermissionField",270            "name": "field",271            "type": "uint8"272          },273          { "internalType": "bool", "name": "value", "type": "bool" }274        ],275        "internalType": "struct CollectionNestingPermission[]",276        "name": "",277        "type": "tuple[]"278      }279    ],280    "stateMutability": "view",281    "type": "function"282  },283  {284    "inputs": [],285    "name": "collectionNestingRestrictedCollectionIds",286    "outputs": [287      {288        "components": [289          { "internalType": "bool", "name": "token_owner", "type": "bool" },290          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }291        ],292        "internalType": "struct CollectionNesting",293        "name": "",294        "type": "tuple"295      }296    ],297    "stateMutability": "view",298    "type": "function"299  },300  {301    "inputs": [],302    "name": "collectionOwner",303    "outputs": [304      {305        "components": [306          { "internalType": "address", "name": "eth", "type": "address" },307          { "internalType": "uint256", "name": "sub", "type": "uint256" }308        ],309        "internalType": "struct CrossAddress",310        "name": "",311        "type": "tuple"312      }313    ],314    "stateMutability": "view",315    "type": "function"316  },317  {318    "inputs": [319      { "internalType": "string[]", "name": "keys", "type": "string[]" }320    ],321    "name": "collectionProperties",322    "outputs": [323      {324        "components": [325          { "internalType": "string", "name": "key", "type": "string" },326          { "internalType": "bytes", "name": "value", "type": "bytes" }327        ],328        "internalType": "struct Property[]",329        "name": "",330        "type": "tuple[]"331      }332    ],333    "stateMutability": "view",334    "type": "function"335  },336  {337    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],338    "name": "collectionProperty",339    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],340    "stateMutability": "view",341    "type": "function"342  },343  {344    "inputs": [],345    "name": "collectionSponsor",346    "outputs": [347      {348        "components": [349          { "internalType": "address", "name": "eth", "type": "address" },350          { "internalType": "uint256", "name": "sub", "type": "uint256" }351        ],352        "internalType": "struct CrossAddress",353        "name": "",354        "type": "tuple"355      }356    ],357    "stateMutability": "view",358    "type": "function"359  },360  {361    "inputs": [],362    "name": "confirmCollectionSponsorship",363    "outputs": [],364    "stateMutability": "nonpayable",365    "type": "function"366  },367  {368    "inputs": [],369    "name": "contractAddress",370    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],371    "stateMutability": "view",372    "type": "function"373  },374  {375    "inputs": [376      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }377    ],378    "name": "crossOwnerOf",379    "outputs": [380      {381        "components": [382          { "internalType": "address", "name": "eth", "type": "address" },383          { "internalType": "uint256", "name": "sub", "type": "uint256" }384        ],385        "internalType": "struct CrossAddress",386        "name": "",387        "type": "tuple"388      }389    ],390    "stateMutability": "view",391    "type": "function"392  },393  {394    "inputs": [395      { "internalType": "string[]", "name": "keys", "type": "string[]" }396    ],397    "name": "deleteCollectionProperties",398    "outputs": [],399    "stateMutability": "nonpayable",400    "type": "function"401  },402  {403    "inputs": [404      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },405      { "internalType": "string[]", "name": "keys", "type": "string[]" }406    ],407    "name": "deleteProperties",408    "outputs": [],409    "stateMutability": "nonpayable",410    "type": "function"411  },412  {413    "inputs": [],414    "name": "description",415    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],416    "stateMutability": "view",417    "type": "function"418  },419  {420    "inputs": [421      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }422    ],423    "name": "getApproved",424    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],425    "stateMutability": "view",426    "type": "function"427  },428  {429    "inputs": [],430    "name": "hasCollectionPendingSponsor",431    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],432    "stateMutability": "view",433    "type": "function"434  },435  {436    "inputs": [437      { "internalType": "address", "name": "owner", "type": "address" },438      { "internalType": "address", "name": "operator", "type": "address" }439    ],440    "name": "isApprovedForAll",441    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],442    "stateMutability": "view",443    "type": "function"444  },445  {446    "inputs": [447      {448        "components": [449          { "internalType": "address", "name": "eth", "type": "address" },450          { "internalType": "uint256", "name": "sub", "type": "uint256" }451        ],452        "internalType": "struct CrossAddress",453        "name": "user",454        "type": "tuple"455      }456    ],457    "name": "isOwnerOrAdminCross",458    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],459    "stateMutability": "view",460    "type": "function"461  },462  {463    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],464    "name": "mint",465    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],466    "stateMutability": "nonpayable",467    "type": "function"468  },469  {470    "inputs": [471      {472        "components": [473          { "internalType": "address", "name": "eth", "type": "address" },474          { "internalType": "uint256", "name": "sub", "type": "uint256" }475        ],476        "internalType": "struct CrossAddress",477        "name": "to",478        "type": "tuple"479      },480      {481        "components": [482          { "internalType": "string", "name": "key", "type": "string" },483          { "internalType": "bytes", "name": "value", "type": "bytes" }484        ],485        "internalType": "struct Property[]",486        "name": "properties",487        "type": "tuple[]"488      }489    ],490    "name": "mintCross",491    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],492    "stateMutability": "nonpayable",493    "type": "function"494  },495  {496    "inputs": [497      { "internalType": "address", "name": "to", "type": "address" },498      { "internalType": "string", "name": "tokenUri", "type": "string" }499    ],500    "name": "mintWithTokenURI",501    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],502    "stateMutability": "nonpayable",503    "type": "function"504  },505  {506    "inputs": [],507    "name": "name",508    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],509    "stateMutability": "view",510    "type": "function"511  },512  {513    "inputs": [],514    "name": "nextTokenId",515    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],516    "stateMutability": "view",517    "type": "function"518  },519  {520    "inputs": [521      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }522    ],523    "name": "ownerOf",524    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],525    "stateMutability": "view",526    "type": "function"527  },528  {529    "inputs": [530      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },531      { "internalType": "string[]", "name": "keys", "type": "string[]" }532    ],533    "name": "properties",534    "outputs": [535      {536        "components": [537          { "internalType": "string", "name": "key", "type": "string" },538          { "internalType": "bytes", "name": "value", "type": "bytes" }539        ],540        "internalType": "struct Property[]",541        "name": "",542        "type": "tuple[]"543      }544    ],545    "stateMutability": "view",546    "type": "function"547  },548  {549    "inputs": [550      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },551      { "internalType": "string", "name": "key", "type": "string" }552    ],553    "name": "property",554    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],555    "stateMutability": "view",556    "type": "function"557  },558  {559    "inputs": [560      {561        "components": [562          { "internalType": "address", "name": "eth", "type": "address" },563          { "internalType": "uint256", "name": "sub", "type": "uint256" }564        ],565        "internalType": "struct CrossAddress",566        "name": "admin",567        "type": "tuple"568      }569    ],570    "name": "removeCollectionAdminCross",571    "outputs": [],572    "stateMutability": "nonpayable",573    "type": "function"574  },575  {576    "inputs": [],577    "name": "removeCollectionSponsor",578    "outputs": [],579    "stateMutability": "nonpayable",580    "type": "function"581  },582  {583    "inputs": [584      {585        "components": [586          { "internalType": "address", "name": "eth", "type": "address" },587          { "internalType": "uint256", "name": "sub", "type": "uint256" }588        ],589        "internalType": "struct CrossAddress",590        "name": "user",591        "type": "tuple"592      }593    ],594    "name": "removeFromCollectionAllowListCross",595    "outputs": [],596    "stateMutability": "nonpayable",597    "type": "function"598  },599  {600    "inputs": [601      { "internalType": "address", "name": "from", "type": "address" },602      { "internalType": "address", "name": "to", "type": "address" },603      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }604    ],605    "name": "safeTransferFrom",606    "outputs": [],607    "stateMutability": "nonpayable",608    "type": "function"609  },610  {611    "inputs": [612      { "internalType": "address", "name": "from", "type": "address" },613      { "internalType": "address", "name": "to", "type": "address" },614      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },615      { "internalType": "bytes", "name": "data", "type": "bytes" }616    ],617    "name": "safeTransferFrom",618    "outputs": [],619    "stateMutability": "nonpayable",620    "type": "function"621  },622  {623    "inputs": [624      { "internalType": "address", "name": "operator", "type": "address" },625      { "internalType": "bool", "name": "approved", "type": "bool" }626    ],627    "name": "setApprovalForAll",628    "outputs": [],629    "stateMutability": "nonpayable",630    "type": "function"631  },632  {633    "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],634    "name": "setCollectionAccess",635    "outputs": [],636    "stateMutability": "nonpayable",637    "type": "function"638  },639  {640    "inputs": [641      {642        "components": [643          {644            "internalType": "enum CollectionLimitField",645            "name": "field",646            "type": "uint8"647          },648          {649            "components": [650              { "internalType": "bool", "name": "status", "type": "bool" },651              { "internalType": "uint256", "name": "value", "type": "uint256" }652            ],653            "internalType": "struct OptionUint256",654            "name": "value",655            "type": "tuple"656          }657        ],658        "internalType": "struct CollectionLimit",659        "name": "limit",660        "type": "tuple"661      }662    ],663    "name": "setCollectionLimit",664    "outputs": [],665    "stateMutability": "nonpayable",666    "type": "function"667  },668  {669    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],670    "name": "setCollectionMintMode",671    "outputs": [],672    "stateMutability": "nonpayable",673    "type": "function"674  },675  {676    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],677    "name": "setCollectionNesting",678    "outputs": [],679    "stateMutability": "nonpayable",680    "type": "function"681  },682  {683    "inputs": [684      { "internalType": "bool", "name": "enable", "type": "bool" },685      {686        "internalType": "address[]",687        "name": "collections",688        "type": "address[]"689      }690    ],691    "name": "setCollectionNesting",692    "outputs": [],693    "stateMutability": "nonpayable",694    "type": "function"695  },696  {697    "inputs": [698      {699        "components": [700          { "internalType": "string", "name": "key", "type": "string" },701          { "internalType": "bytes", "name": "value", "type": "bytes" }702        ],703        "internalType": "struct Property[]",704        "name": "properties",705        "type": "tuple[]"706      }707    ],708    "name": "setCollectionProperties",709    "outputs": [],710    "stateMutability": "nonpayable",711    "type": "function"712  },713  {714    "inputs": [715      {716        "components": [717          { "internalType": "address", "name": "eth", "type": "address" },718          { "internalType": "uint256", "name": "sub", "type": "uint256" }719        ],720        "internalType": "struct CrossAddress",721        "name": "sponsor",722        "type": "tuple"723      }724    ],725    "name": "setCollectionSponsorCross",726    "outputs": [],727    "stateMutability": "nonpayable",728    "type": "function"729  },730  {731    "inputs": [732      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },733      {734        "components": [735          { "internalType": "string", "name": "key", "type": "string" },736          { "internalType": "bytes", "name": "value", "type": "bytes" }737        ],738        "internalType": "struct Property[]",739        "name": "properties",740        "type": "tuple[]"741      }742    ],743    "name": "setProperties",744    "outputs": [],745    "stateMutability": "nonpayable",746    "type": "function"747  },748  {749    "inputs": [750      {751        "components": [752          { "internalType": "string", "name": "key", "type": "string" },753          {754            "components": [755              {756                "internalType": "enum TokenPermissionField",757                "name": "code",758                "type": "uint8"759              },760              { "internalType": "bool", "name": "value", "type": "bool" }761            ],762            "internalType": "struct PropertyPermission[]",763            "name": "permissions",764            "type": "tuple[]"765          }766        ],767        "internalType": "struct TokenPropertyPermission[]",768        "name": "permissions",769        "type": "tuple[]"770      }771    ],772    "name": "setTokenPropertyPermissions",773    "outputs": [],774    "stateMutability": "nonpayable",775    "type": "function"776  },777  {778    "inputs": [779      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }780    ],781    "name": "supportsInterface",782    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],783    "stateMutability": "view",784    "type": "function"785  },786  {787    "inputs": [],788    "name": "symbol",789    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],790    "stateMutability": "view",791    "type": "function"792  },793  {794    "inputs": [795      { "internalType": "uint256", "name": "index", "type": "uint256" }796    ],797    "name": "tokenByIndex",798    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],799    "stateMutability": "view",800    "type": "function"801  },802  {803    "inputs": [804      { "internalType": "address", "name": "owner", "type": "address" },805      { "internalType": "uint256", "name": "index", "type": "uint256" }806    ],807    "name": "tokenOfOwnerByIndex",808    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],809    "stateMutability": "view",810    "type": "function"811  },812  {813    "inputs": [],814    "name": "tokenPropertyPermissions",815    "outputs": [816      {817        "components": [818          { "internalType": "string", "name": "key", "type": "string" },819          {820            "components": [821              {822                "internalType": "enum TokenPermissionField",823                "name": "code",824                "type": "uint8"825              },826              { "internalType": "bool", "name": "value", "type": "bool" }827            ],828            "internalType": "struct PropertyPermission[]",829            "name": "permissions",830            "type": "tuple[]"831          }832        ],833        "internalType": "struct TokenPropertyPermission[]",834        "name": "",835        "type": "tuple[]"836      }837    ],838    "stateMutability": "view",839    "type": "function"840  },841  {842    "inputs": [843      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }844    ],845    "name": "tokenURI",846    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],847    "stateMutability": "view",848    "type": "function"849  },850  {851    "inputs": [],852    "name": "totalSupply",853    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],854    "stateMutability": "view",855    "type": "function"856  },857  {858    "inputs": [859      { "internalType": "address", "name": "to", "type": "address" },860      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }861    ],862    "name": "transfer",863    "outputs": [],864    "stateMutability": "nonpayable",865    "type": "function"866  },867  {868    "inputs": [869      {870        "components": [871          { "internalType": "address", "name": "eth", "type": "address" },872          { "internalType": "uint256", "name": "sub", "type": "uint256" }873        ],874        "internalType": "struct CrossAddress",875        "name": "to",876        "type": "tuple"877      },878      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }879    ],880    "name": "transferCross",881    "outputs": [],882    "stateMutability": "nonpayable",883    "type": "function"884  },885  {886    "inputs": [887      { "internalType": "address", "name": "from", "type": "address" },888      { "internalType": "address", "name": "to", "type": "address" },889      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }890    ],891    "name": "transferFrom",892    "outputs": [],893    "stateMutability": "nonpayable",894    "type": "function"895  },896  {897    "inputs": [898      {899        "components": [900          { "internalType": "address", "name": "eth", "type": "address" },901          { "internalType": "uint256", "name": "sub", "type": "uint256" }902        ],903        "internalType": "struct CrossAddress",904        "name": "from",905        "type": "tuple"906      },907      {908        "components": [909          { "internalType": "address", "name": "eth", "type": "address" },910          { "internalType": "uint256", "name": "sub", "type": "uint256" }911        ],912        "internalType": "struct CrossAddress",913        "name": "to",914        "type": "tuple"915      },916      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }917    ],918    "name": "transferFromCross",919    "outputs": [],920    "stateMutability": "nonpayable",921    "type": "function"922  },923  {924    "inputs": [],925    "name": "uniqueCollectionType",926    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],927    "stateMutability": "view",928    "type": "function"929  }930]
modifiedtests/src/eth/abi/reFungible.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -51,6 +51,12 @@
   },
   {
     "anonymous": false,
+    "inputs": [],
+    "name": "MintingFinished",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
     "inputs": [
       {
         "indexed": true,
@@ -399,6 +405,13 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "finishMinting",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
@@ -486,6 +499,13 @@
   },
   {
     "inputs": [],
+    "name": "mintingFinished",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
     "name": "name",
     "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
     "stateMutability": "view",
@@ -596,7 +616,7 @@
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
       { "internalType": "bytes", "name": "data", "type": "bytes" }
     ],
-    "name": "safeTransferFromWithData",
+    "name": "safeTransferFrom",
     "outputs": [],
     "stateMutability": "nonpayable",
     "type": "function"
modifiedtests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/reFungibleToken.json
+++ b/tests/src/eth/abi/reFungibleToken.json
@@ -98,6 +98,16 @@
   },
   {
     "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
       {
         "components": [
           { "internalType": "address", "name": "eth", "type": "address" },
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -536,7 +536,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x0e48fdb4
+/// @dev the ERC-165 identifier for this interface is 0x16de3152
 interface ERC721UniqueExtensions is Dummy, ERC165 {
 	/// @notice A descriptive name for a collection of NFTs in this contract
 	/// @dev EVM selector for this function is: 0x06fdde03,
@@ -661,6 +661,11 @@
 	/// @dev EVM selector for this function is: 0xb904db03,
 	///  or in textual repr: mintCross((address,uint256),(string,bytes)[])
 	function mintCross(CrossAddress memory to, Property[] memory properties) external returns (uint256);
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() external view returns (address);
 }
 
 /// @dev anonymous struct
@@ -703,7 +708,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x983a942b
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
 interface ERC721 is Dummy, ERC165, ERC721Events {
 	/// @notice Count all NFTs assigned to an owner
 	/// @dev NFTs assigned to the zero address are considered invalid, and this
@@ -786,11 +791,6 @@
 	/// @dev EVM selector for this function is: 0xe985e9c5,
 	///  or in textual repr: isApprovedForAll(address,address)
 	function isApprovedForAll(address owner, address operator) external view returns (bool);
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() external view returns (address);
 }
 
 interface UniqueNFT is
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -441,6 +441,8 @@
 	TransferEnabled
 }
 
+/// @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
@@ -534,7 +536,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xabf30dc2
+/// @dev the ERC-165 identifier for this interface is 0xb365c124
 interface ERC721UniqueExtensions is Dummy, ERC165 {
 	/// @notice A descriptive name for a collection of NFTs in this contract
 	/// @dev EVM selector for this function is: 0x06fdde03,
@@ -660,6 +662,11 @@
 	/// @dev EVM selector for this function is: 0xab76fac6,
 	///  or in textual repr: tokenContractAddress(uint256)
 	function tokenContractAddress(uint256 token) external view returns (address);
+
+	/// @notice Returns collection helper contract address
+	/// @dev EVM selector for this function is: 0x1896cce6,
+	///  or in textual repr: collectionHelperAddress()
+	function collectionHelperAddress() external view returns (address);
 }
 
 /// @dev anonymous struct
@@ -702,7 +709,7 @@
 
 /// @title ERC-721 Non-Fungible Token Standard
 /// @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md
-/// @dev the ERC-165 identifier for this interface is 0x4016cd87
+/// @dev the ERC-165 identifier for this interface is 0x80ac58cd
 interface ERC721 is Dummy, ERC165, ERC721Events {
 	/// @notice Count all RFTs assigned to an owner
 	/// @dev RFTs assigned to the zero address are considered invalid, and this
@@ -725,9 +732,9 @@
 	function ownerOf(uint256 tokenId) external view returns (address);
 
 	/// @dev Not implemented
-	/// @dev EVM selector for this function is: 0x60a11672,
-	///  or in textual repr: safeTransferFromWithData(address,address,uint256,bytes)
-	function safeTransferFromWithData(
+	/// @dev EVM selector for this function is: 0xb88d4fde,
+	///  or in textual repr: safeTransferFrom(address,address,uint256,bytes)
+	function safeTransferFrom(
 		address from,
 		address to,
 		uint256 tokenId,
@@ -783,11 +790,6 @@
 	/// @dev EVM selector for this function is: 0xe985e9c5,
 	///  or in textual repr: isApprovedForAll(address,address)
 	function isApprovedForAll(address owner, address operator) external view returns (bool);
-
-	/// @notice Returns collection helper contract address
-	/// @dev EVM selector for this function is: 0x1896cce6,
-	///  or in textual repr: collectionHelperAddress()
-	function collectionHelperAddress() external view returns (address);
 }
 
 interface UniqueRefungible is