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
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 { .. } => 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(_, _) => None,269270			// Sponsored271			Mint { .. }272			| MintCheckId { .. }273			| MintWithTokenUri { .. }274			| MintWithTokenUriCheckId { .. } => withdraw_create_item::<T>(275				&collection,276				&who,277				&CreateItemData::NFT(CreateNftData::default()),278			),279		}280	}281}282283/// Module for methods of refungible token284///285/// Existance of token should be checked before searching for sponsor286/// because RefungibleTokenHandle doesn't check token's existence upon creation287mod erc20 {288	use super::*;289290	pub fn call_sponsor<T>(291		call: ERC20Call<T>,292		token: RefungibleTokenHandle<T>,293		who: &T::CrossAccountId,294	) -> Option<()>295	where296		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,297	{298		use ERC20Call::*;299300		match call {301			// Readonly302			ERC165Call(_, _)303			| Name304			| Symbol305			| TotalSupply306			| Decimals307			| BalanceOf { .. }308			| Allowance { .. } => None,309310			Transfer { .. } => {311				let RefungibleTokenHandle(handle, token_id) = token;312				let token_id = token_id.try_into().ok()?;313				withdraw_transfer::<T>(&handle, &who, &token_id)314			}315			TransferFrom { from, .. } => {316				let RefungibleTokenHandle(handle, token_id) = token;317				let token_id = token_id.try_into().ok()?;318				let from = T::CrossAccountId::from_eth(from);319				withdraw_transfer::<T>(&handle, &from, &token_id)320			}321			Approve { .. } => {322				let RefungibleTokenHandle(handle, token_id) = token;323				let token_id = token_id.try_into().ok()?;324				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)325			}326		}327	}328329	pub fn unique_extensions_call_sponsor<T>(330		call: ERC20UniqueExtensionsCall<T>,331		token: RefungibleTokenHandle<T>,332		who: &T::CrossAccountId,333	) -> Option<()>334	where335		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,336	{337		use ERC20UniqueExtensionsCall::*;338339		match call {340			// Readonly341			ERC165Call(_, _) => None,342343			// Not sponsored344			AllowanceCross { .. } | BurnFrom { .. } | BurnFromCross { .. } | Repartition { .. } => {345				None346			}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			| OwnerOfCross { .. }231			| BalanceOfCross { .. }232			| Properties { .. }233			| NextTokenId234			| TokenContractAddress { .. }235			| CollectionHelperAddress => None,236237			// Not sponsored238			BurnFrom { .. }239			| BurnFromCross { .. }240			| MintBulk { .. }241			| MintBulkWithTokenUri { .. } => None,242243			MintCross { .. } => withdraw_create_item::<T>(244				&collection,245				&who,246				&CreateItemData::NFT(CreateNftData::default()),247			),248249			TransferCross { token_id, .. }250			| TransferFromCross { token_id, .. }251			| Transfer { token_id, .. } => {252				let token_id = TokenId::try_from(token_id).ok()?;253				withdraw_transfer::<T>(&collection, &who, &token_id)254			}255		}256	}257258	pub fn unique_mintable_call_sponsor<T>(259		call: ERC721UniqueMintableCall<T>,260		collection: CollectionHandle<T>,261		who: &T::CrossAccountId,262	) -> Option<()>263	where264		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,265	{266		use ERC721UniqueMintableCall::*;267268		match call {269			// Readonly270			ERC165Call(_, _) => None,271272			// Sponsored273			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			AllowanceCross { .. }347			| BalanceOfCross { .. }348			| BurnFrom { .. }349			| BurnFromCross { .. }350			| Repartition { .. } => None,351352			TransferCross { .. } | TransferFromCross { .. } => {353				let RefungibleTokenHandle(handle, token_id) = token;354				let token_id = token_id.try_into().ok()?;355				withdraw_transfer::<T>(&handle, &who, &token_id)356			}357358			ApproveCross { .. } => {359				let RefungibleTokenHandle(handle, token_id) = token;360				let token_id = token_id.try_into().ok()?;361				withdraw_approve::<T>(&handle, who.as_sub(), &token_id)362			}363		}364	}365}366367mod erc1633 {368	use super::*;369370	pub fn call_sponsor<T>(371		call: ERC1633Call<T>,372		_token: RefungibleTokenHandle<T>,373		_who: &T::CrossAccountId,374	) -> Option<()>375	where376		T: UniqueConfig + FungibleConfig + NonfungibleConfig + RefungibleConfig,377	{378		use ERC1633Call::*;379380		match call {381			// Readonly382			ERC165Call(_, _) | ParentToken | ParentTokenId => None,383		}384	}385}
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
--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -177,6 +177,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",
@@ -386,25 +403,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",
@@ -540,6 +538,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/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();