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
before · runtime/common/ethereum/sponsoring/refungible.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implements EVM sponsoring logic via TransactionValidityHack1819use core::convert::TryInto;20use pallet_common::CollectionHandle;21use pallet_evm::account::CrossAccountId;22use pallet_fungible::Config as FungibleConfig;23use pallet_refungible::Config as RefungibleConfig;24use pallet_nonfungible::Config as NonfungibleConfig;25use pallet_unique::Config as UniqueConfig;26use up_data_structs::{CreateItemData, CreateNftData, TokenId};2728use super::common;29use crate::runtime_common::sponsoring::*;3031use pallet_refungible::{32	erc::{33		ERC721BurnableCall, ERC721Call, ERC721EnumerableCall, ERC721MetadataCall,34		ERC721UniqueExtensionsCall, ERC721UniqueMintableCall, TokenPropertiesCall,35		UniqueRefungibleCall,36	},37	erc_token::{38		ERC1633Call, ERC20Call, ERC20UniqueExtensionsCall, RefungibleTokenHandle,39		UniqueRefungibleTokenCall,40	},41};4243pub fn call_sponsor<T>(44	call: UniqueRefungibleCall<T>,45	collection: CollectionHandle<T>,46	who: &T::CrossAccountId,47) -> Option<()>48where49	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,50{51	use UniqueRefungibleCall::*;5253	match call {54		// Readonly55		ERC165Call(_, _) => None,5657		ERC721Enumerable(call) => erc721::enumerable_call_sponsor(call, collection, who),58		ERC721Burnable(call) => erc721::burnable_call_sponsor(call, collection, who),59		ERC721Metadata(call) => erc721::metadata_call_sponsor(call, collection, who),60		Collection(call) => common::collection_call_sponsor(call, collection, who),61		ERC721(call) => erc721::call_sponsor(call, collection, who),62		ERC721UniqueExtensions(call) => {63			erc721::unique_extensions_call_sponsor(call, collection, who)64		}65		ERC721UniqueMintable(call) => erc721::unique_mintable_call_sponsor(call, collection, who),66		TokenProperties(call) => token_properties_call_sponsor(call, collection, who),67	}68}6970pub fn token_properties_call_sponsor<T>(71	call: TokenPropertiesCall<T>,72	collection: CollectionHandle<T>,73	who: &T::CrossAccountId,74) -> Option<()>75where76	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,77{78	use TokenPropertiesCall::*;7980	match call {81		// Readonly82		ERC165Call(_, _) | Property { .. } | TokenPropertyPermissions => None,8384		// Not sponsored85		SetTokenPropertyPermission { .. }86		| SetTokenPropertyPermissions { .. }87		| SetProperties { .. }88		| DeleteProperty { .. }89		| DeleteProperties { .. } => None,9091		SetProperty {92			token_id,93			key,94			value,95			..96		} => {97			let token_id = TokenId::try_from(token_id).ok()?;98			withdraw_set_token_property::<T>(&collection, &who, &token_id, key.len() + value.len())99		}100	}101}102103pub fn token_call_sponsor<T>(104	call: UniqueRefungibleTokenCall<T>,105	token: RefungibleTokenHandle<T>,106	who: &T::CrossAccountId,107) -> Option<()>108where109	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,110{111	use UniqueRefungibleTokenCall::*;112113	match call {114		// Readonly115		ERC165Call(_, _) => None,116117		ERC20(call) => erc20::call_sponsor(call, token, who),118		ERC20UniqueExtensions(call) => erc20::unique_extensions_call_sponsor(call, token, who),119		ERC1633(call) => erc1633::call_sponsor(call, token, who),120	}121}122123mod erc721 {124	use super::*;125126	pub fn call_sponsor<T>(127		call: ERC721Call<T>,128		collection: CollectionHandle<T>,129		_who: &T::CrossAccountId,130	) -> Option<()>131	where132		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,133	{134		use ERC721Call::*;135136		match call {137			// Readonly138			ERC165Call(_, _)139			| BalanceOf { .. }140			| OwnerOf { .. }141			| GetApproved { .. }142			| IsApprovedForAll { .. }143			| CollectionHelperAddress => None,144145			// Not sponsored146			SafeTransferFromWithData { .. }147			| SafeTransferFrom { .. }148			| SetApprovalForAll { .. } => None,149150			TransferFrom { token_id, from, .. } => {151				let token_id = TokenId::try_from(token_id).ok()?;152				let from = T::CrossAccountId::from_eth(from);153				withdraw_transfer::<T>(&collection, &from, &token_id)154			}155156			// Not supported157			Approve { .. } => None,158		}159	}160161	pub fn enumerable_call_sponsor<T>(162		call: ERC721EnumerableCall<T>,163		_collection: CollectionHandle<T>,164		_who: &T::CrossAccountId,165	) -> Option<()>166	where167		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,168	{169		use ERC721EnumerableCall::*;170171		match call {172			// Readonly173			ERC165Call(_, _) | TokenByIndex { .. } | TokenOfOwnerByIndex { .. } | TotalSupply => {174				None175			}176		}177	}178179	pub fn burnable_call_sponsor<T>(180		call: ERC721BurnableCall<T>,181		_collection: CollectionHandle<T>,182		_who: &T::CrossAccountId,183	) -> Option<()>184	where185		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,186	{187		use ERC721BurnableCall::*;188189		match call {190			// Readonly191			ERC165Call(_, _) => None,192193			// Not sponsored194			Burn { .. } => None,195		}196	}197198	pub fn metadata_call_sponsor<T>(199		call: ERC721MetadataCall<T>,200		_collection: CollectionHandle<T>,201		_who: &T::CrossAccountId,202	) -> Option<()>203	where204		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,205	{206		use ERC721MetadataCall::*;207208		match call {209			// Readonly210			ERC165Call(_, _) | NameProxy | SymbolProxy | TokenUri { .. } => None,211		}212	}213214	pub fn unique_extensions_call_sponsor<T>(215		call: ERC721UniqueExtensionsCall<T>,216		collection: CollectionHandle<T>,217		who: &T::CrossAccountId,218	) -> Option<()>219	where220		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,221	{222		use ERC721UniqueExtensionsCall::*;223224		match call {225			// Readonly226			ERC165Call(_, _)227			| Name228			| Symbol229			| Description230			| CrossOwnerOf { .. }231			| Properties { .. }232			| NextTokenId233			| TokenContractAddress { .. } => None,234235			// Not sponsored236			BurnFrom { .. }237			| BurnFromCross { .. }238			| MintBulk { .. }239			| MintBulkWithTokenUri { .. } => None,240241			MintCross { .. } => withdraw_create_item::<T>(242				&collection,243				&who,244				&CreateItemData::NFT(CreateNftData::default()),245			),246247			TransferCross { token_id, .. }248			| TransferFromCross { token_id, .. }249			| Transfer { token_id, .. } => {250				let token_id = TokenId::try_from(token_id).ok()?;251				withdraw_transfer::<T>(&collection, &who, &token_id)252			}253		}254	}255256	pub fn unique_mintable_call_sponsor<T>(257		call: ERC721UniqueMintableCall<T>,258		collection: CollectionHandle<T>,259		who: &T::CrossAccountId,260	) -> Option<()>261	where262		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,263	{264		use ERC721UniqueMintableCall::*;265266		match call {267			// Readonly268			ERC165Call(_, _) | MintingFinished => None,269270			// Not sponsored271			FinishMinting => None,272273			Mint { .. }274			| MintCheckId { .. }275			| MintWithTokenUri { .. }276			| MintWithTokenUriCheckId { .. } => withdraw_create_item::<T>(277				&collection,278				&who,279				&CreateItemData::NFT(CreateNftData::default()),280			),281		}282	}283}284285/// Module for methods of refungible token286///287/// Existance of token should be checked before searching for sponsor288/// because RefungibleTokenHandle doesn't check token's existence upon creation289mod erc20 {290	use super::*;291292	pub fn call_sponsor<T>(293		call: ERC20Call<T>,294		token: RefungibleTokenHandle<T>,295		who: &T::CrossAccountId,296	) -> Option<()>297	where298		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,299	{300		use ERC20Call::*;301302		match call {303			// Readonly304			ERC165Call(_, _)305			| Name306			| Symbol307			| TotalSupply308			| Decimals309			| BalanceOf { .. }310			| Allowance { .. } => None,311312			Transfer { .. } => {313				let RefungibleTokenHandle(handle, token_id) = token;314				let token_id = token_id.try_into().ok()?;315				withdraw_transfer::<T>(&handle, &who, &token_id)316			}317			TransferFrom { from, .. } => {318				let RefungibleTokenHandle(handle, token_id) = token;319				let token_id = token_id.try_into().ok()?;320				let from = T::CrossAccountId::from_eth(from);321				withdraw_transfer::<T>(&handle, &from, &token_id)322			}323			Approve { .. } => {324				let RefungibleTokenHandle(handle, token_id) = token;325				let token_id = token_id.try_into().ok()?;326				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)327			}328		}329	}330331	pub fn unique_extensions_call_sponsor<T>(332		call: ERC20UniqueExtensionsCall<T>,333		token: RefungibleTokenHandle<T>,334		who: &T::CrossAccountId,335	) -> Option<()>336	where337		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,338	{339		use ERC20UniqueExtensionsCall::*;340341		match call {342			// Readonly343			ERC165Call(_, _) => None,344345			// Not sponsored346			BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => None,347348			TransferCross { .. } | TransferFromCross { .. } => {349				let RefungibleTokenHandle(handle, token_id) = token;350				let token_id = token_id.try_into().ok()?;351				withdraw_transfer::<T>(&handle, &who, &token_id)352			}353354			ApproveCross { .. } => {355				let RefungibleTokenHandle(handle, token_id) = token;356				let token_id = token_id.try_into().ok()?;357				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)358			}359		}360	}361}362363mod erc1633 {364	use super::*;365366	pub fn call_sponsor<T>(367		call: ERC1633Call<T>,368		_token: RefungibleTokenHandle<T>,369		_who: &T::CrossAccountId,370	) -> Option<()>371	where372		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,373	{374		use ERC1633Call::*;375376		match call {377			// Readonly378			ERC165Call(_, _) | ParentToken | ParentTokenId => None,379		}380	}381}
after · runtime/common/ethereum/sponsoring/refungible.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! Implements EVM sponsoring logic via TransactionValidityHack1819use core::convert::TryInto;20use pallet_common::CollectionHandle;21use pallet_evm::account::CrossAccountId;22use pallet_fungible::Config as FungibleConfig;23use pallet_refungible::Config as RefungibleConfig;24use pallet_nonfungible::Config as NonfungibleConfig;25use pallet_unique::Config as UniqueConfig;26use up_data_structs::{CreateItemData, CreateNftData, TokenId};2728use super::common;29use crate::runtime_common::sponsoring::*;3031use pallet_refungible::{32	erc::{33		ERC721BurnableCall, ERC721Call, ERC721EnumerableCall, ERC721MetadataCall,34		ERC721UniqueExtensionsCall, ERC721UniqueMintableCall, TokenPropertiesCall,35		UniqueRefungibleCall,36	},37	erc_token::{38		ERC1633Call, ERC20Call, ERC20UniqueExtensionsCall, RefungibleTokenHandle,39		UniqueRefungibleTokenCall,40	},41};4243pub fn call_sponsor<T>(44	call: UniqueRefungibleCall<T>,45	collection: CollectionHandle<T>,46	who: &T::CrossAccountId,47) -> Option<()>48where49	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,50{51	use UniqueRefungibleCall::*;5253	match call {54		// Readonly55		ERC165Call(_, _) => None,5657		ERC721Enumerable(call) => erc721::enumerable_call_sponsor(call, collection, who),58		ERC721Burnable(call) => erc721::burnable_call_sponsor(call, collection, who),59		ERC721Metadata(call) => erc721::metadata_call_sponsor(call, collection, who),60		Collection(call) => common::collection_call_sponsor(call, collection, who),61		ERC721(call) => erc721::call_sponsor(call, collection, who),62		ERC721UniqueExtensions(call) => {63			erc721::unique_extensions_call_sponsor(call, collection, who)64		}65		ERC721UniqueMintable(call) => erc721::unique_mintable_call_sponsor(call, collection, who),66		TokenProperties(call) => token_properties_call_sponsor(call, collection, who),67	}68}6970pub fn token_properties_call_sponsor<T>(71	call: TokenPropertiesCall<T>,72	collection: CollectionHandle<T>,73	who: &T::CrossAccountId,74) -> Option<()>75where76	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,77{78	use TokenPropertiesCall::*;7980	match call {81		// Readonly82		ERC165Call(_, _) | Property { .. } | TokenPropertyPermissions => None,8384		// Not sponsored85		SetTokenPropertyPermission { .. }86		| SetTokenPropertyPermissions { .. }87		| SetProperties { .. }88		| DeleteProperty { .. }89		| DeleteProperties { .. } => None,9091		SetProperty {92			token_id,93			key,94			value,95			..96		} => {97			let token_id = TokenId::try_from(token_id).ok()?;98			withdraw_set_token_property::<T>(&collection, &who, &token_id, key.len() + value.len())99		}100	}101}102103pub fn token_call_sponsor<T>(104	call: UniqueRefungibleTokenCall<T>,105	token: RefungibleTokenHandle<T>,106	who: &T::CrossAccountId,107) -> Option<()>108where109	T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,110{111	use UniqueRefungibleTokenCall::*;112113	match call {114		// Readonly115		ERC165Call(_, _) => None,116117		ERC20(call) => erc20::call_sponsor(call, token, who),118		ERC20UniqueExtensions(call) => erc20::unique_extensions_call_sponsor(call, token, who),119		ERC1633(call) => erc1633::call_sponsor(call, token, who),120	}121}122123mod erc721 {124	use super::*;125126	pub fn call_sponsor<T>(127		call: ERC721Call<T>,128		collection: CollectionHandle<T>,129		_who: &T::CrossAccountId,130	) -> Option<()>131	where132		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,133	{134		use ERC721Call::*;135136		match call {137			// Readonly138			ERC165Call(_, _)139			| BalanceOf { .. }140			| OwnerOf { .. }141			| GetApproved { .. }142			| IsApprovedForAll { .. } => None,143144			// Not sponsored145			SafeTransferFromWithData { .. }146			| SafeTransferFrom { .. }147			| SetApprovalForAll { .. } => None,148149			TransferFrom { token_id, from, .. } => {150				let token_id = TokenId::try_from(token_id).ok()?;151				let from = T::CrossAccountId::from_eth(from);152				withdraw_transfer::<T>(&collection, &from, &token_id)153			}154155			// Not supported156			Approve { .. } => None,157		}158	}159160	pub fn enumerable_call_sponsor<T>(161		call: ERC721EnumerableCall<T>,162		_collection: CollectionHandle<T>,163		_who: &T::CrossAccountId,164	) -> Option<()>165	where166		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,167	{168		use ERC721EnumerableCall::*;169170		match call {171			// Readonly172			ERC165Call(_, _) | TokenByIndex { .. } | TokenOfOwnerByIndex { .. } | TotalSupply => {173				None174			}175		}176	}177178	pub fn burnable_call_sponsor<T>(179		call: ERC721BurnableCall<T>,180		_collection: CollectionHandle<T>,181		_who: &T::CrossAccountId,182	) -> Option<()>183	where184		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,185	{186		use ERC721BurnableCall::*;187188		match call {189			// Readonly190			ERC165Call(_, _) => None,191192			// Not sponsored193			Burn { .. } => None,194		}195	}196197	pub fn metadata_call_sponsor<T>(198		call: ERC721MetadataCall<T>,199		_collection: CollectionHandle<T>,200		_who: &T::CrossAccountId,201	) -> Option<()>202	where203		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,204	{205		use ERC721MetadataCall::*;206207		match call {208			// Readonly209			ERC165Call(_, _) | NameProxy | SymbolProxy | TokenUri { .. } => None,210		}211	}212213	pub fn unique_extensions_call_sponsor<T>(214		call: ERC721UniqueExtensionsCall<T>,215		collection: CollectionHandle<T>,216		who: &T::CrossAccountId,217	) -> Option<()>218	where219		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,220	{221		use ERC721UniqueExtensionsCall::*;222223		match call {224			// Readonly225			ERC165Call(_, _)226			| Name227			| Symbol228			| Description229			| CrossOwnerOf { .. }230			| Properties { .. }231			| NextTokenId232			| TokenContractAddress { .. }233			| CollectionHelperAddress => None,234235			// Not sponsored236			BurnFrom { .. }237			| BurnFromCross { .. }238			| MintBulk { .. }239			| MintBulkWithTokenUri { .. } => None,240241			MintCross { .. } => withdraw_create_item::<T>(242				&collection,243				&who,244				&CreateItemData::NFT(CreateNftData::default()),245			),246247			TransferCross { token_id, .. }248			| TransferFromCross { token_id, .. }249			| Transfer { token_id, .. } => {250				let token_id = TokenId::try_from(token_id).ok()?;251				withdraw_transfer::<T>(&collection, &who, &token_id)252			}253		}254	}255256	pub fn unique_mintable_call_sponsor<T>(257		call: ERC721UniqueMintableCall<T>,258		collection: CollectionHandle<T>,259		who: &T::CrossAccountId,260	) -> Option<()>261	where262		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,263	{264		use ERC721UniqueMintableCall::*;265266		match call {267			// Readonly268			ERC165Call(_, _) | MintingFinished => None,269270			// Not sponsored271			FinishMinting => None,272273			Mint { .. }274			| MintCheckId { .. }275			| MintWithTokenUri { .. }276			| MintWithTokenUriCheckId { .. } => withdraw_create_item::<T>(277				&collection,278				&who,279				&CreateItemData::NFT(CreateNftData::default()),280			),281		}282	}283}284285/// Module for methods of refungible token286///287/// Existance of token should be checked before searching for sponsor288/// because RefungibleTokenHandle doesn't check token's existence upon creation289mod erc20 {290	use super::*;291292	pub fn call_sponsor<T>(293		call: ERC20Call<T>,294		token: RefungibleTokenHandle<T>,295		who: &T::CrossAccountId,296	) -> Option<()>297	where298		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,299	{300		use ERC20Call::*;301302		match call {303			// Readonly304			ERC165Call(_, _)305			| Name306			| Symbol307			| TotalSupply308			| Decimals309			| BalanceOf { .. }310			| Allowance { .. } => None,311312			Transfer { .. } => {313				let RefungibleTokenHandle(handle, token_id) = token;314				let token_id = token_id.try_into().ok()?;315				withdraw_transfer::<T>(&handle, &who, &token_id)316			}317			TransferFrom { from, .. } => {318				let RefungibleTokenHandle(handle, token_id) = token;319				let token_id = token_id.try_into().ok()?;320				let from = T::CrossAccountId::from_eth(from);321				withdraw_transfer::<T>(&handle, &from, &token_id)322			}323			Approve { .. } => {324				let RefungibleTokenHandle(handle, token_id) = token;325				let token_id = token_id.try_into().ok()?;326				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)327			}328		}329	}330331	pub fn unique_extensions_call_sponsor<T>(332		call: ERC20UniqueExtensionsCall<T>,333		token: RefungibleTokenHandle<T>,334		who: &T::CrossAccountId,335	) -> Option<()>336	where337		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,338	{339		use ERC20UniqueExtensionsCall::*;340341		match call {342			// Readonly343			ERC165Call(_, _) => None,344345			// Not sponsored346			BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => None,347348			TransferCross { .. } | TransferFromCross { .. } => {349				let RefungibleTokenHandle(handle, token_id) = token;350				let token_id = token_id.try_into().ok()?;351				withdraw_transfer::<T>(&handle, &who, &token_id)352			}353354			ApproveCross { .. } => {355				let RefungibleTokenHandle(handle, token_id) = token;356				let token_id = token_id.try_into().ok()?;357				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)358			}359		}360	}361}362363mod erc1633 {364	use super::*;365366	pub fn call_sponsor<T>(367		call: ERC1633Call<T>,368		_token: RefungibleTokenHandle<T>,369		_who: &T::CrossAccountId,370	) -> Option<()>371	where372		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,373	{374		use ERC1633Call::*;375376		match call {377			// Readonly378			ERC165Call(_, _) | ParentToken | ParentTokenId => None,379		}380	}381}
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
--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -51,6 +51,12 @@
   },
   {
     "anonymous": false,
+    "inputs": [],
+    "name": "MintingFinished",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
     "inputs": [
       {
         "indexed": true,
@@ -417,6 +423,13 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "finishMinting",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
@@ -504,6 +517,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",
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