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

difftreelog

Merge pull request #919 from UniqueNetwork/feature/crossBalanceOf

Yaroslav Bolyukin2023-04-19parents: #c894432 #a1d8979.patch.diff
in: master

26 files changed

modifiedpallets/fungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -327,6 +327,15 @@
 	fn collection_helper_address(&self) -> Result<Address> {
 		Ok(T::ContractAddress::get())
 	}
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {
+		self.consume_store_reads(1)?;
+		let balance = <Balance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));
+		Ok(balance.into())
+	}
 }
 
 #[solidity_interface(
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -511,7 +511,7 @@
 	bytes value;
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x85d7dea6
+/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
 contract ERC20UniqueExtensions is Dummy, ERC165 {
 	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
 	/// @param owner crossAddress The address which owns the funds.
@@ -630,6 +630,18 @@
 		dummy;
 		return 0x0000000000000000000000000000000000000000;
 	}
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
 }
 
 struct AmountForAddress {
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -749,12 +749,29 @@
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
+	#[solidity(hide)]
 	fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
+		Self::owner_of_cross(&self, token_id)
+	}
+
+	/// Returns the owner (in cross format) of the token.
+	///
+	/// @param tokenId Id for the token.
+	fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
 		Self::token_owner(&self, token_id.try_into()?)
 			.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
 			.map_err(|_| Error::Revert("token not found".into()))
 	}
 
+	/// @notice Count all NFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {
+		self.consume_store_reads(1)?;
+		let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));
+		Ok(balance.into())
+	}
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
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
@@ -774,7 +774,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x16de3152
+/// @dev the ERC-165 identifier for this interface is 0x307b061a
 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,
@@ -803,18 +803,42 @@
 		return "";
 	}
 
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+	// 	require(false, stub_error);
+	// 	tokenId;
+	// 	dummy;
+	// 	return CrossAddress(0x0000000000000000000000000000000000000000,0);
+	// }
+
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0x2b29dace,
-	///  or in textual repr: crossOwnerOf(uint256)
-	function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) public view returns (CrossAddress memory) {
 		require(false, stub_error);
 		tokenId;
 		dummy;
 		return CrossAddress(0x0000000000000000000000000000000000000000, 0);
 	}
 
+	/// @notice Count all NFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -783,7 +783,15 @@
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
+	#[solidity(hide)]
 	fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {
+		Self::owner_of_cross(&self, token_id)
+	}
+
+	/// Returns the owner (in cross format) of the token.
+	///
+	/// @param tokenId Id for the token.
+	fn owner_of_cross(&self, token_id: U256) -> Result<eth::CrossAddress> {
 		Self::token_owner(&self, token_id.try_into()?)
 			.map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))
 			.or_else(|err| match err {
@@ -794,6 +802,15 @@
 			})
 	}
 
+	/// @notice Count all RFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	fn balance_of_cross(&self, owner: eth::CrossAddress) -> Result<U256> {
+		self.consume_store_reads(1)?;
+		let balance = <AccountBalance<T>>::get((self.id, owner.into_sub_cross_account::<T>()?));
+		Ok(balance.into())
+	}
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
modifiedpallets/refungible/src/erc_token.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc_token.rs
+++ b/pallets/refungible/src/erc_token.rs
@@ -283,6 +283,16 @@
 			.map_err(dispatch_to_evm::<T>)?;
 		Ok(true)
 	}
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {
+		self.consume_store_reads(1)?;
+		let balance = <Balance<T>>::get((self.id, self.1, owner.into_sub_cross_account::<T>()?));
+		Ok(balance.into())
+	}
+
 	/// @dev Function that changes total amount of the tokens.
 	///  Throws if `msg.sender` doesn't owns all of the tokens.
 	/// @param amount New total amount of the tokens.
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
@@ -774,7 +774,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb365c124
+/// @dev the ERC-165 identifier for this interface is 0x95c0f66c
 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,
@@ -803,18 +803,42 @@
 		return "";
 	}
 
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+	// 	require(false, stub_error);
+	// 	tokenId;
+	// 	dummy;
+	// 	return CrossAddress(0x0000000000000000000000000000000000000000,0);
+	// }
+
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0x2b29dace,
-	///  or in textual repr: crossOwnerOf(uint256)
-	function crossOwnerOf(uint256 tokenId) public view returns (CrossAddress memory) {
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) public view returns (CrossAddress memory) {
 		require(false, stub_error);
 		tokenId;
 		dummy;
 		return CrossAddress(0x0000000000000000000000000000000000000000, 0);
 	}
 
+	/// @notice Count all RFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungibleToken.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungibleToken.sol
@@ -36,7 +36,7 @@
 	}
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x01d536fc
+/// @dev the ERC-165 identifier for this interface is 0xedd3a564
 contract ERC20UniqueExtensions is Dummy, ERC165 {
 	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
 	/// @param owner crossAddress The address which owns the funds.
@@ -97,6 +97,18 @@
 		return false;
 	}
 
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) public view returns (uint256) {
+		require(false, stub_error);
+		owner;
+		dummy;
+		return 0;
+	}
+
 	/// @dev Function that changes total amount of the tokens.
 	///  Throws if `msg.sender` doesn't owns all of the tokens.
 	/// @param amount New total amount of the tokens.
modifiedruntime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth
--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -227,6 +227,8 @@
 			| Symbol
 			| Description
 			| CrossOwnerOf { .. }
+			| OwnerOfCross { .. }
+			| BalanceOfCross { .. }
 			| Properties { .. }
 			| NextTokenId
 			| TokenContractAddress { .. }
@@ -341,9 +343,11 @@
 			ERC165Call(_, _) => None,
 
 			// Not sponsored
-			AllowanceCross { .. } | BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => {
-				None
-			}
+			AllowanceCross { .. }
+			| BalanceOfCross { .. }
+			| BurnFrom { .. }
+			| BurnFromCross { .. }
+			| Repartition { .. } => None,
 
 			TransferCross { .. } | TransferFromCross { .. } => {
 				let RefungibleTokenHandle(handle, token_id) = token;
modifiedtests/src/eth/abi/fungible.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/fungible.json
+++ b/tests/src/eth/abi/fungible.json
@@ -181,6 +181,23 @@
           { "internalType": "uint256", "name": "sub", "type": "uint256" }
         ],
         "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
         "name": "from",
         "type": "tuple"
       },
modifiedtests/src/eth/abi/nonFungible.jsondiffbeforeafterboth
after · 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": "uint256",58        "name": "tokenId",59        "type": "uint256"60      }61    ],62    "name": "TokenChanged",63    "type": "event"64  },65  {66    "anonymous": false,67    "inputs": [68      {69        "indexed": true,70        "internalType": "address",71        "name": "from",72        "type": "address"73      },74      {75        "indexed": true,76        "internalType": "address",77        "name": "to",78        "type": "address"79      },80      {81        "indexed": true,82        "internalType": "uint256",83        "name": "tokenId",84        "type": "uint256"85      }86    ],87    "name": "Transfer",88    "type": "event"89  },90  {91    "inputs": [92      {93        "components": [94          { "internalType": "address", "name": "eth", "type": "address" },95          { "internalType": "uint256", "name": "sub", "type": "uint256" }96        ],97        "internalType": "struct CrossAddress",98        "name": "newAdmin",99        "type": "tuple"100      }101    ],102    "name": "addCollectionAdminCross",103    "outputs": [],104    "stateMutability": "nonpayable",105    "type": "function"106  },107  {108    "inputs": [109      {110        "components": [111          { "internalType": "address", "name": "eth", "type": "address" },112          { "internalType": "uint256", "name": "sub", "type": "uint256" }113        ],114        "internalType": "struct CrossAddress",115        "name": "user",116        "type": "tuple"117      }118    ],119    "name": "addToCollectionAllowListCross",120    "outputs": [],121    "stateMutability": "nonpayable",122    "type": "function"123  },124  {125    "inputs": [126      {127        "components": [128          { "internalType": "address", "name": "eth", "type": "address" },129          { "internalType": "uint256", "name": "sub", "type": "uint256" }130        ],131        "internalType": "struct CrossAddress",132        "name": "user",133        "type": "tuple"134      }135    ],136    "name": "allowlistedCross",137    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],138    "stateMutability": "view",139    "type": "function"140  },141  {142    "inputs": [143      { "internalType": "address", "name": "approved", "type": "address" },144      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }145    ],146    "name": "approve",147    "outputs": [],148    "stateMutability": "nonpayable",149    "type": "function"150  },151  {152    "inputs": [153      {154        "components": [155          { "internalType": "address", "name": "eth", "type": "address" },156          { "internalType": "uint256", "name": "sub", "type": "uint256" }157        ],158        "internalType": "struct CrossAddress",159        "name": "approved",160        "type": "tuple"161      },162      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }163    ],164    "name": "approveCross",165    "outputs": [],166    "stateMutability": "nonpayable",167    "type": "function"168  },169  {170    "inputs": [171      { "internalType": "address", "name": "owner", "type": "address" }172    ],173    "name": "balanceOf",174    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],175    "stateMutability": "view",176    "type": "function"177  },178  {179    "inputs": [180      {181        "components": [182          { "internalType": "address", "name": "eth", "type": "address" },183          { "internalType": "uint256", "name": "sub", "type": "uint256" }184        ],185        "internalType": "struct CrossAddress",186        "name": "owner",187        "type": "tuple"188      }189    ],190    "name": "balanceOfCross",191    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],192    "stateMutability": "view",193    "type": "function"194  },195  {196    "inputs": [197      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }198    ],199    "name": "burn",200    "outputs": [],201    "stateMutability": "nonpayable",202    "type": "function"203  },204  {205    "inputs": [206      {207        "components": [208          { "internalType": "address", "name": "eth", "type": "address" },209          { "internalType": "uint256", "name": "sub", "type": "uint256" }210        ],211        "internalType": "struct CrossAddress",212        "name": "from",213        "type": "tuple"214      },215      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }216    ],217    "name": "burnFromCross",218    "outputs": [],219    "stateMutability": "nonpayable",220    "type": "function"221  },222  {223    "inputs": [224      {225        "components": [226          { "internalType": "address", "name": "eth", "type": "address" },227          { "internalType": "uint256", "name": "sub", "type": "uint256" }228        ],229        "internalType": "struct CrossAddress",230        "name": "newOwner",231        "type": "tuple"232      }233    ],234    "name": "changeCollectionOwnerCross",235    "outputs": [],236    "stateMutability": "nonpayable",237    "type": "function"238  },239  {240    "inputs": [],241    "name": "collectionAdmins",242    "outputs": [243      {244        "components": [245          { "internalType": "address", "name": "eth", "type": "address" },246          { "internalType": "uint256", "name": "sub", "type": "uint256" }247        ],248        "internalType": "struct CrossAddress[]",249        "name": "",250        "type": "tuple[]"251      }252    ],253    "stateMutability": "view",254    "type": "function"255  },256  {257    "inputs": [],258    "name": "collectionHelperAddress",259    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],260    "stateMutability": "view",261    "type": "function"262  },263  {264    "inputs": [],265    "name": "collectionLimits",266    "outputs": [267      {268        "components": [269          {270            "internalType": "enum CollectionLimitField",271            "name": "field",272            "type": "uint8"273          },274          {275            "components": [276              { "internalType": "bool", "name": "status", "type": "bool" },277              { "internalType": "uint256", "name": "value", "type": "uint256" }278            ],279            "internalType": "struct OptionUint256",280            "name": "value",281            "type": "tuple"282          }283        ],284        "internalType": "struct CollectionLimit[]",285        "name": "",286        "type": "tuple[]"287      }288    ],289    "stateMutability": "view",290    "type": "function"291  },292  {293    "inputs": [],294    "name": "collectionNestingPermissions",295    "outputs": [296      {297        "components": [298          {299            "internalType": "enum CollectionPermissionField",300            "name": "field",301            "type": "uint8"302          },303          { "internalType": "bool", "name": "value", "type": "bool" }304        ],305        "internalType": "struct CollectionNestingPermission[]",306        "name": "",307        "type": "tuple[]"308      }309    ],310    "stateMutability": "view",311    "type": "function"312  },313  {314    "inputs": [],315    "name": "collectionNestingRestrictedCollectionIds",316    "outputs": [317      {318        "components": [319          { "internalType": "bool", "name": "token_owner", "type": "bool" },320          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }321        ],322        "internalType": "struct CollectionNesting",323        "name": "",324        "type": "tuple"325      }326    ],327    "stateMutability": "view",328    "type": "function"329  },330  {331    "inputs": [],332    "name": "collectionOwner",333    "outputs": [334      {335        "components": [336          { "internalType": "address", "name": "eth", "type": "address" },337          { "internalType": "uint256", "name": "sub", "type": "uint256" }338        ],339        "internalType": "struct CrossAddress",340        "name": "",341        "type": "tuple"342      }343    ],344    "stateMutability": "view",345    "type": "function"346  },347  {348    "inputs": [349      { "internalType": "string[]", "name": "keys", "type": "string[]" }350    ],351    "name": "collectionProperties",352    "outputs": [353      {354        "components": [355          { "internalType": "string", "name": "key", "type": "string" },356          { "internalType": "bytes", "name": "value", "type": "bytes" }357        ],358        "internalType": "struct Property[]",359        "name": "",360        "type": "tuple[]"361      }362    ],363    "stateMutability": "view",364    "type": "function"365  },366  {367    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],368    "name": "collectionProperty",369    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],370    "stateMutability": "view",371    "type": "function"372  },373  {374    "inputs": [],375    "name": "collectionSponsor",376    "outputs": [377      {378        "components": [379          { "internalType": "address", "name": "eth", "type": "address" },380          { "internalType": "uint256", "name": "sub", "type": "uint256" }381        ],382        "internalType": "struct CrossAddress",383        "name": "",384        "type": "tuple"385      }386    ],387    "stateMutability": "view",388    "type": "function"389  },390  {391    "inputs": [],392    "name": "confirmCollectionSponsorship",393    "outputs": [],394    "stateMutability": "nonpayable",395    "type": "function"396  },397  {398    "inputs": [],399    "name": "contractAddress",400    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],401    "stateMutability": "view",402    "type": "function"403  },404  {405    "inputs": [406      { "internalType": "string[]", "name": "keys", "type": "string[]" }407    ],408    "name": "deleteCollectionProperties",409    "outputs": [],410    "stateMutability": "nonpayable",411    "type": "function"412  },413  {414    "inputs": [415      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },416      { "internalType": "string[]", "name": "keys", "type": "string[]" }417    ],418    "name": "deleteProperties",419    "outputs": [],420    "stateMutability": "nonpayable",421    "type": "function"422  },423  {424    "inputs": [],425    "name": "description",426    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],427    "stateMutability": "view",428    "type": "function"429  },430  {431    "inputs": [432      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }433    ],434    "name": "getApproved",435    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],436    "stateMutability": "view",437    "type": "function"438  },439  {440    "inputs": [],441    "name": "hasCollectionPendingSponsor",442    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],443    "stateMutability": "view",444    "type": "function"445  },446  {447    "inputs": [448      { "internalType": "address", "name": "owner", "type": "address" },449      { "internalType": "address", "name": "operator", "type": "address" }450    ],451    "name": "isApprovedForAll",452    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],453    "stateMutability": "view",454    "type": "function"455  },456  {457    "inputs": [458      {459        "components": [460          { "internalType": "address", "name": "eth", "type": "address" },461          { "internalType": "uint256", "name": "sub", "type": "uint256" }462        ],463        "internalType": "struct CrossAddress",464        "name": "user",465        "type": "tuple"466      }467    ],468    "name": "isOwnerOrAdminCross",469    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],470    "stateMutability": "view",471    "type": "function"472  },473  {474    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],475    "name": "mint",476    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],477    "stateMutability": "nonpayable",478    "type": "function"479  },480  {481    "inputs": [482      {483        "components": [484          { "internalType": "address", "name": "eth", "type": "address" },485          { "internalType": "uint256", "name": "sub", "type": "uint256" }486        ],487        "internalType": "struct CrossAddress",488        "name": "to",489        "type": "tuple"490      },491      {492        "components": [493          { "internalType": "string", "name": "key", "type": "string" },494          { "internalType": "bytes", "name": "value", "type": "bytes" }495        ],496        "internalType": "struct Property[]",497        "name": "properties",498        "type": "tuple[]"499      }500    ],501    "name": "mintCross",502    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],503    "stateMutability": "nonpayable",504    "type": "function"505  },506  {507    "inputs": [508      { "internalType": "address", "name": "to", "type": "address" },509      { "internalType": "string", "name": "tokenUri", "type": "string" }510    ],511    "name": "mintWithTokenURI",512    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],513    "stateMutability": "nonpayable",514    "type": "function"515  },516  {517    "inputs": [],518    "name": "name",519    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],520    "stateMutability": "view",521    "type": "function"522  },523  {524    "inputs": [],525    "name": "nextTokenId",526    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],527    "stateMutability": "view",528    "type": "function"529  },530  {531    "inputs": [532      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }533    ],534    "name": "ownerOf",535    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],536    "stateMutability": "view",537    "type": "function"538  },539  {540    "inputs": [541      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }542    ],543    "name": "ownerOfCross",544    "outputs": [545      {546        "components": [547          { "internalType": "address", "name": "eth", "type": "address" },548          { "internalType": "uint256", "name": "sub", "type": "uint256" }549        ],550        "internalType": "struct CrossAddress",551        "name": "",552        "type": "tuple"553      }554    ],555    "stateMutability": "view",556    "type": "function"557  },558  {559    "inputs": [560      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },561      { "internalType": "string[]", "name": "keys", "type": "string[]" }562    ],563    "name": "properties",564    "outputs": [565      {566        "components": [567          { "internalType": "string", "name": "key", "type": "string" },568          { "internalType": "bytes", "name": "value", "type": "bytes" }569        ],570        "internalType": "struct Property[]",571        "name": "",572        "type": "tuple[]"573      }574    ],575    "stateMutability": "view",576    "type": "function"577  },578  {579    "inputs": [580      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },581      { "internalType": "string", "name": "key", "type": "string" }582    ],583    "name": "property",584    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],585    "stateMutability": "view",586    "type": "function"587  },588  {589    "inputs": [590      {591        "components": [592          { "internalType": "address", "name": "eth", "type": "address" },593          { "internalType": "uint256", "name": "sub", "type": "uint256" }594        ],595        "internalType": "struct CrossAddress",596        "name": "admin",597        "type": "tuple"598      }599    ],600    "name": "removeCollectionAdminCross",601    "outputs": [],602    "stateMutability": "nonpayable",603    "type": "function"604  },605  {606    "inputs": [],607    "name": "removeCollectionSponsor",608    "outputs": [],609    "stateMutability": "nonpayable",610    "type": "function"611  },612  {613    "inputs": [614      {615        "components": [616          { "internalType": "address", "name": "eth", "type": "address" },617          { "internalType": "uint256", "name": "sub", "type": "uint256" }618        ],619        "internalType": "struct CrossAddress",620        "name": "user",621        "type": "tuple"622      }623    ],624    "name": "removeFromCollectionAllowListCross",625    "outputs": [],626    "stateMutability": "nonpayable",627    "type": "function"628  },629  {630    "inputs": [631      { "internalType": "address", "name": "from", "type": "address" },632      { "internalType": "address", "name": "to", "type": "address" },633      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }634    ],635    "name": "safeTransferFrom",636    "outputs": [],637    "stateMutability": "nonpayable",638    "type": "function"639  },640  {641    "inputs": [642      { "internalType": "address", "name": "from", "type": "address" },643      { "internalType": "address", "name": "to", "type": "address" },644      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },645      { "internalType": "bytes", "name": "data", "type": "bytes" }646    ],647    "name": "safeTransferFrom",648    "outputs": [],649    "stateMutability": "nonpayable",650    "type": "function"651  },652  {653    "inputs": [654      { "internalType": "address", "name": "operator", "type": "address" },655      { "internalType": "bool", "name": "approved", "type": "bool" }656    ],657    "name": "setApprovalForAll",658    "outputs": [],659    "stateMutability": "nonpayable",660    "type": "function"661  },662  {663    "inputs": [664      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }665    ],666    "name": "setCollectionAccess",667    "outputs": [],668    "stateMutability": "nonpayable",669    "type": "function"670  },671  {672    "inputs": [673      {674        "components": [675          {676            "internalType": "enum CollectionLimitField",677            "name": "field",678            "type": "uint8"679          },680          {681            "components": [682              { "internalType": "bool", "name": "status", "type": "bool" },683              { "internalType": "uint256", "name": "value", "type": "uint256" }684            ],685            "internalType": "struct OptionUint256",686            "name": "value",687            "type": "tuple"688          }689        ],690        "internalType": "struct CollectionLimit",691        "name": "limit",692        "type": "tuple"693      }694    ],695    "name": "setCollectionLimit",696    "outputs": [],697    "stateMutability": "nonpayable",698    "type": "function"699  },700  {701    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],702    "name": "setCollectionMintMode",703    "outputs": [],704    "stateMutability": "nonpayable",705    "type": "function"706  },707  {708    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],709    "name": "setCollectionNesting",710    "outputs": [],711    "stateMutability": "nonpayable",712    "type": "function"713  },714  {715    "inputs": [716      { "internalType": "bool", "name": "enable", "type": "bool" },717      {718        "internalType": "address[]",719        "name": "collections",720        "type": "address[]"721      }722    ],723    "name": "setCollectionNesting",724    "outputs": [],725    "stateMutability": "nonpayable",726    "type": "function"727  },728  {729    "inputs": [730      {731        "components": [732          { "internalType": "string", "name": "key", "type": "string" },733          { "internalType": "bytes", "name": "value", "type": "bytes" }734        ],735        "internalType": "struct Property[]",736        "name": "properties",737        "type": "tuple[]"738      }739    ],740    "name": "setCollectionProperties",741    "outputs": [],742    "stateMutability": "nonpayable",743    "type": "function"744  },745  {746    "inputs": [747      {748        "components": [749          { "internalType": "address", "name": "eth", "type": "address" },750          { "internalType": "uint256", "name": "sub", "type": "uint256" }751        ],752        "internalType": "struct CrossAddress",753        "name": "sponsor",754        "type": "tuple"755      }756    ],757    "name": "setCollectionSponsorCross",758    "outputs": [],759    "stateMutability": "nonpayable",760    "type": "function"761  },762  {763    "inputs": [764      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },765      {766        "components": [767          { "internalType": "string", "name": "key", "type": "string" },768          { "internalType": "bytes", "name": "value", "type": "bytes" }769        ],770        "internalType": "struct Property[]",771        "name": "properties",772        "type": "tuple[]"773      }774    ],775    "name": "setProperties",776    "outputs": [],777    "stateMutability": "nonpayable",778    "type": "function"779  },780  {781    "inputs": [782      {783        "components": [784          { "internalType": "string", "name": "key", "type": "string" },785          {786            "components": [787              {788                "internalType": "enum TokenPermissionField",789                "name": "code",790                "type": "uint8"791              },792              { "internalType": "bool", "name": "value", "type": "bool" }793            ],794            "internalType": "struct PropertyPermission[]",795            "name": "permissions",796            "type": "tuple[]"797          }798        ],799        "internalType": "struct TokenPropertyPermission[]",800        "name": "permissions",801        "type": "tuple[]"802      }803    ],804    "name": "setTokenPropertyPermissions",805    "outputs": [],806    "stateMutability": "nonpayable",807    "type": "function"808  },809  {810    "inputs": [811      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }812    ],813    "name": "supportsInterface",814    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],815    "stateMutability": "view",816    "type": "function"817  },818  {819    "inputs": [],820    "name": "symbol",821    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],822    "stateMutability": "view",823    "type": "function"824  },825  {826    "inputs": [827      { "internalType": "uint256", "name": "index", "type": "uint256" }828    ],829    "name": "tokenByIndex",830    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],831    "stateMutability": "view",832    "type": "function"833  },834  {835    "inputs": [836      { "internalType": "address", "name": "owner", "type": "address" },837      { "internalType": "uint256", "name": "index", "type": "uint256" }838    ],839    "name": "tokenOfOwnerByIndex",840    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],841    "stateMutability": "view",842    "type": "function"843  },844  {845    "inputs": [],846    "name": "tokenPropertyPermissions",847    "outputs": [848      {849        "components": [850          { "internalType": "string", "name": "key", "type": "string" },851          {852            "components": [853              {854                "internalType": "enum TokenPermissionField",855                "name": "code",856                "type": "uint8"857              },858              { "internalType": "bool", "name": "value", "type": "bool" }859            ],860            "internalType": "struct PropertyPermission[]",861            "name": "permissions",862            "type": "tuple[]"863          }864        ],865        "internalType": "struct TokenPropertyPermission[]",866        "name": "",867        "type": "tuple[]"868      }869    ],870    "stateMutability": "view",871    "type": "function"872  },873  {874    "inputs": [875      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }876    ],877    "name": "tokenURI",878    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],879    "stateMutability": "view",880    "type": "function"881  },882  {883    "inputs": [],884    "name": "totalSupply",885    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],886    "stateMutability": "view",887    "type": "function"888  },889  {890    "inputs": [891      { "internalType": "address", "name": "to", "type": "address" },892      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }893    ],894    "name": "transfer",895    "outputs": [],896    "stateMutability": "nonpayable",897    "type": "function"898  },899  {900    "inputs": [901      {902        "components": [903          { "internalType": "address", "name": "eth", "type": "address" },904          { "internalType": "uint256", "name": "sub", "type": "uint256" }905        ],906        "internalType": "struct CrossAddress",907        "name": "to",908        "type": "tuple"909      },910      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }911    ],912    "name": "transferCross",913    "outputs": [],914    "stateMutability": "nonpayable",915    "type": "function"916  },917  {918    "inputs": [919      { "internalType": "address", "name": "from", "type": "address" },920      { "internalType": "address", "name": "to", "type": "address" },921      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }922    ],923    "name": "transferFrom",924    "outputs": [],925    "stateMutability": "nonpayable",926    "type": "function"927  },928  {929    "inputs": [930      {931        "components": [932          { "internalType": "address", "name": "eth", "type": "address" },933          { "internalType": "uint256", "name": "sub", "type": "uint256" }934        ],935        "internalType": "struct CrossAddress",936        "name": "from",937        "type": "tuple"938      },939      {940        "components": [941          { "internalType": "address", "name": "eth", "type": "address" },942          { "internalType": "uint256", "name": "sub", "type": "uint256" }943        ],944        "internalType": "struct CrossAddress",945        "name": "to",946        "type": "tuple"947      },948      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }949    ],950    "name": "transferFromCross",951    "outputs": [],952    "stateMutability": "nonpayable",953    "type": "function"954  },955  {956    "inputs": [],957    "name": "uniqueCollectionType",958    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],959    "stateMutability": "view",960    "type": "function"961  }962]
modifiedtests/src/eth/abi/reFungible.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -159,6 +159,23 @@
   },
   {
     "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
     "name": "burn",
@@ -368,25 +385,6 @@
   },
   {
     "inputs": [
-      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
-    ],
-    "name": "crossOwnerOf",
-    "outputs": [
-      {
-        "components": [
-          { "internalType": "address", "name": "eth", "type": "address" },
-          { "internalType": "uint256", "name": "sub", "type": "uint256" }
-        ],
-        "internalType": "struct CrossAddress",
-        "name": "",
-        "type": "tuple"
-      }
-    ],
-    "stateMutability": "view",
-    "type": "function"
-  },
-  {
-    "inputs": [
       { "internalType": "string[]", "name": "keys", "type": "string[]" }
     ],
     "name": "deleteCollectionProperties",
@@ -522,6 +520,25 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOfCross",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
       { "internalType": "string[]", "name": "keys", "type": "string[]" }
     ],
modifiedtests/src/eth/abi/reFungibleToken.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/reFungibleToken.json
+++ b/tests/src/eth/abi/reFungibleToken.json
@@ -130,6 +130,23 @@
           { "internalType": "uint256", "name": "sub", "type": "uint256" }
         ],
         "internalType": "struct CrossAddress",
+        "name": "owner",
+        "type": "tuple"
+      }
+    ],
+    "name": "balanceOfCross",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "eth", "type": "address" },
+          { "internalType": "uint256", "name": "sub", "type": "uint256" }
+        ],
+        "internalType": "struct CrossAddress",
         "name": "from",
         "type": "tuple"
       },
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -353,7 +353,7 @@
 	bytes value;
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x85d7dea6
+/// @dev the ERC-165 identifier for this interface is 0x69d14d3e
 interface ERC20UniqueExtensions is Dummy, ERC165 {
 	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
 	/// @param owner crossAddress The address which owns the funds.
@@ -416,6 +416,13 @@
 	/// @dev EVM selector for this function is: 0x1896cce6,
 	///  or in textual repr: collectionHelperAddress()
 	function collectionHelperAddress() external view returns (address);
+
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
 }
 
 struct AmountForAddress {
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -533,7 +533,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0x16de3152
+/// @dev the ERC-165 identifier for this interface is 0x307b061a
 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,
@@ -550,12 +550,26 @@
 	///  or in textual repr: description()
 	function description() external view returns (string memory);
 
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0x2b29dace,
-	///  or in textual repr: crossOwnerOf(uint256)
-	function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// @notice Count all NFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
 
 	/// Returns the token properties.
 	///
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -533,7 +533,7 @@
 }
 
 /// @title Unique extensions for ERC721.
-/// @dev the ERC-165 identifier for this interface is 0xb365c124
+/// @dev the ERC-165 identifier for this interface is 0x95c0f66c
 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,
@@ -550,12 +550,26 @@
 	///  or in textual repr: description()
 	function description() external view returns (string memory);
 
+	// /// Returns the owner (in cross format) of the token.
+	// ///
+	// /// @param tokenId Id for the token.
+	// /// @dev EVM selector for this function is: 0x2b29dace,
+	// ///  or in textual repr: crossOwnerOf(uint256)
+	// function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+
 	/// Returns the owner (in cross format) of the token.
 	///
 	/// @param tokenId Id for the token.
-	/// @dev EVM selector for this function is: 0x2b29dace,
-	///  or in textual repr: crossOwnerOf(uint256)
-	function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
+	/// @dev EVM selector for this function is: 0xcaa3a4d0,
+	///  or in textual repr: ownerOfCross(uint256)
+	function ownerOfCross(uint256 tokenId) external view returns (CrossAddress memory);
+
+	/// @notice Count all RFTs assigned to an owner
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
 
 	/// Returns the token properties.
 	///
modifiedtests/src/eth/api/UniqueRefungibleToken.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungibleToken.sol
+++ b/tests/src/eth/api/UniqueRefungibleToken.sol
@@ -23,7 +23,7 @@
 	function parentTokenId() external view returns (uint256);
 }
 
-/// @dev the ERC-165 identifier for this interface is 0x01d536fc
+/// @dev the ERC-165 identifier for this interface is 0xedd3a564
 interface ERC20UniqueExtensions is Dummy, ERC165 {
 	/// @dev Function to check the amount of tokens that an owner allowed to a spender.
 	/// @param owner crossAddress The address which owns the funds.
@@ -60,6 +60,13 @@
 	///  or in textual repr: approveCross((address,uint256),uint256)
 	function approveCross(CrossAddress memory spender, uint256 amount) external returns (bool);
 
+	/// @notice Balance of account
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of fingibles owned by `owner`, possibly zero
+	/// @dev EVM selector for this function is: 0xec069398,
+	///  or in textual repr: balanceOfCross((address,uint256))
+	function balanceOfCross(CrossAddress memory owner) external view returns (uint256);
+
 	/// @dev Function that changes total amount of the tokens.
 	///  Throws if `msg.sender` doesn't owns all of the tokens.
 	/// @param amount New total amount of the tokens.
modifiedtests/src/eth/fungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -427,6 +427,33 @@
     const toBalanceAfter = await collection.getBalance({Ethereum: receiver});
     expect(toBalanceAfter - toBalanceBefore).to.be.eq(51n);
   });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.ft.mintCollection(alice, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'ft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await collection.mint(alice, 100n, {Ethereum: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+    await collectionEvm.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+    await collectionEvm.methods.transferCross(owner, 100n).send({from: other.eth});
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await collectionEvm.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+  });
 });
 
 describe('Fungible: Fees', () => {
modifiedtests/src/eth/nonFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -662,6 +662,38 @@
     // Cannot transfer token if it does not exist:
     await expect(collectionEvm.methods[testCase](receiver, 999999).send({from: sender})).to.be.rejected;
   }));
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+    for (let i = 1; i < 10; i++) {
+      await collection.mintToken(minter, {Ethereum: owner.eth});
+      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+    }
+  });
+
+  itEth('Check ownerOfCross()', async ({helper}) => {
+    const collection = await helper.nft.mintCollection(minter, {});
+    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+    const {tokenId} = await collection.mintToken(minter, {Ethereum: owner.eth});
+
+    for (let i = 1n; i < 10n; i++) {
+      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+      expect(ownerCross.eth).to.be.eq(owner.eth);
+      expect(ownerCross.sub).to.be.eq(owner.sub);
+
+      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+      owner = newOwner;
+    }
+  });
 });
 
 describe('NFT: Fees', () => {
modifiedtests/src/eth/reFungible.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungible.test.ts
+++ b/tests/src/eth/reFungible.test.ts
@@ -584,6 +584,46 @@
     expect(event.returnValues.to).to.equal('0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF');
     expect(event.returnValues.tokenId).to.equal(tokenId.toString());
   });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+
+    expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+
+    for (let i = 1n; i < 10n; i++) {
+      await collection.mintToken(minter, 100n, {Ethereum: owner.eth});
+      expect(await collectionEvm.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq(i.toString());
+    }
+  });
+
+  itEth('Check ownerOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(minter, {});
+    let owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const collectionEvm = await helper.ethNativeContract.collection(collectionAddress, 'nft', owner.eth);
+    const {tokenId} = await collection.mintToken(minter, 100n,{Ethereum: owner.eth});
+
+    for (let i = 1n; i < 10n; i++) {
+      const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+      expect(ownerCross.eth).to.be.eq(owner.eth);
+      expect(ownerCross.sub).to.be.eq(owner.sub);
+
+      const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      await collectionEvm.methods.transferCross(newOwner, tokenId).send({from: owner.eth});
+      owner = newOwner;
+    }
+
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth, true);
+    const newOwner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    await tokenContract.methods.transferCross(newOwner, 50).send({from: owner.eth});
+    const ownerCross = await collectionEvm.methods.ownerOfCross(tokenId).call({from: owner.eth});
+    expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
+    expect(ownerCross.sub).to.be.eq('0');
+  });
 });
 
 describe('RFT: Fees', () => {
modifiedtests/src/eth/reFungibleToken.test.tsdiffbeforeafterboth
--- a/tests/src/eth/reFungibleToken.test.ts
+++ b/tests/src/eth/reFungibleToken.test.ts
@@ -466,6 +466,35 @@
       expect(await collection.getTokenBalance(tokenId, {Substrate: ownerSub.address})).to.be.equal(150n);
     }
   });
+
+  itEth('Check balanceOfCross()', async ({helper}) => {
+    const collection = await helper.rft.mintCollection(alice, {});
+    const owner = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const other = await helper.ethCrossAccount.createAccountWithBalance(donor);
+    const {tokenId} = await collection.mintToken(alice, 200n, {Ethereum: owner.eth});
+    const tokenAddress = helper.ethAddress.fromTokenId(collection.collectionId, tokenId);
+    const tokenContract = await helper.ethNativeContract.rftToken(tokenAddress, owner.eth);
+
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('200');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await tokenContract.methods.repartition(100n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('100');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('0');
+
+    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('50');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('50');
+
+    await tokenContract.methods.transferCross(other, 50n).send({from: owner.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('0');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('100');
+
+    await tokenContract.methods.repartition(1000n).send({from: other.eth});
+    await tokenContract.methods.transferCross(owner, 500n).send({from: other.eth});
+    expect(await tokenContract.methods.balanceOfCross(owner).call({from: owner.eth})).to.be.eq('500');
+    expect(await tokenContract.methods.balanceOfCross(other).call({from: owner.eth})).to.be.eq('500');
+  });
 });
 
 describe('Refungible: Fees', () => {
modifiedtests/src/eth/tokens/minting.test.tsdiffbeforeafterboth
--- a/tests/src/eth/tokens/minting.test.ts
+++ b/tests/src/eth/tokens/minting.test.ts
@@ -63,7 +63,7 @@
         const tokenId = event.returnValues.tokenId;
         expect(tokenId).to.be.equal('1');
         expect(await helper.collection.getLastTokenId(collection.collectionId)).to.eq(1);
-        expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+        expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
       }
     });
   });
@@ -97,7 +97,7 @@
         const tokenId = event.returnValues.tokenId;
         expect(tokenId).to.be.equal('1');
         expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
-        expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
       }
     });
   });
@@ -131,7 +131,7 @@
         const tokenId = event.returnValues.tokenId;
         expect(tokenId).to.be.equal('1');
         expect(await helper.collection.getLastTokenId(collectionId)).to.eq(1);
-        expect(await collection.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+        expect(await collection.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
       }
     });
   });
@@ -157,7 +157,7 @@
       expect(event.returnValues.to).to.be.equal(receiver);
 
       expect(await contract.methods.tokenURI(tokenId).call()).to.be.equal('Test URI');
-      expect(await contract.methods.crossOwnerOf(tokenId).call()).to.be.like([receiver, '0']);
+      expect(await contract.methods.ownerOfCross(tokenId).call()).to.be.like([receiver, '0']);
       // TODO: this wont work right now, need release 919000 first
       // await helper.methods.setOffchainSchema(collectionIdAddress, 'https://offchain-service.local/token-info/{id}').send();
       // const tokenUri = await contract.methods.tokenURI(nextTokenId).call();