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

difftreelog

feat add crossBalanceOf

Trubnikov Sergey2023-04-17parent: #ac9b5ee.patch.diff
in: master

11 files changed

modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -755,6 +755,17 @@
 			.map_err(|_| Error::Revert("token not found".into()))
 	}
 
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of NFTs owned by `owner`, possibly zero
+	fn cross_balance_of(&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 0x323b1db8
 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,
@@ -815,6 +815,20 @@
 		return CrossAddress(0x0000000000000000000000000000000000000000, 0);
 	}
 
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @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: 0x24e52cea,
+	///  or in textual repr: crossBalanceOf((address,uint256))
+	function crossBalanceOf(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
@@ -794,6 +794,17 @@
 			})
 	}
 
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @param owner An cross address for whom to query the balance
+	/// @return The number of RFTs owned by `owner`, possibly zero
+	fn cross_balance_of(&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/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 0x9780edce
 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,
@@ -815,6 +815,20 @@
 		return CrossAddress(0x0000000000000000000000000000000000000000, 0);
 	}
 
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @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: 0x24e52cea,
+	///  or in textual repr: crossBalanceOf((address,uint256))
+	function crossBalanceOf(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.
modifiedruntime/common/ethereum/sponsoring/refungible.rsdiffbeforeafterboth
--- a/runtime/common/ethereum/sponsoring/refungible.rs
+++ b/runtime/common/ethereum/sponsoring/refungible.rs
@@ -227,6 +227,7 @@
 			| Symbol
 			| Description
 			| CrossOwnerOf { .. }
+			| CrossBalanceOf { .. }
 			| Properties { .. }
 			| NextTokenId
 			| TokenContractAddress { .. }
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      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }181    ],182    "name": "burn",183    "outputs": [],184    "stateMutability": "nonpayable",185    "type": "function"186  },187  {188    "inputs": [189      {190        "components": [191          { "internalType": "address", "name": "eth", "type": "address" },192          { "internalType": "uint256", "name": "sub", "type": "uint256" }193        ],194        "internalType": "struct CrossAddress",195        "name": "from",196        "type": "tuple"197      },198      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }199    ],200    "name": "burnFromCross",201    "outputs": [],202    "stateMutability": "nonpayable",203    "type": "function"204  },205  {206    "inputs": [207      {208        "components": [209          { "internalType": "address", "name": "eth", "type": "address" },210          { "internalType": "uint256", "name": "sub", "type": "uint256" }211        ],212        "internalType": "struct CrossAddress",213        "name": "newOwner",214        "type": "tuple"215      }216    ],217    "name": "changeCollectionOwnerCross",218    "outputs": [],219    "stateMutability": "nonpayable",220    "type": "function"221  },222  {223    "inputs": [],224    "name": "collectionAdmins",225    "outputs": [226      {227        "components": [228          { "internalType": "address", "name": "eth", "type": "address" },229          { "internalType": "uint256", "name": "sub", "type": "uint256" }230        ],231        "internalType": "struct CrossAddress[]",232        "name": "",233        "type": "tuple[]"234      }235    ],236    "stateMutability": "view",237    "type": "function"238  },239  {240    "inputs": [],241    "name": "collectionHelperAddress",242    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],243    "stateMutability": "view",244    "type": "function"245  },246  {247    "inputs": [],248    "name": "collectionLimits",249    "outputs": [250      {251        "components": [252          {253            "internalType": "enum CollectionLimitField",254            "name": "field",255            "type": "uint8"256          },257          {258            "components": [259              { "internalType": "bool", "name": "status", "type": "bool" },260              { "internalType": "uint256", "name": "value", "type": "uint256" }261            ],262            "internalType": "struct OptionUint256",263            "name": "value",264            "type": "tuple"265          }266        ],267        "internalType": "struct CollectionLimit[]",268        "name": "",269        "type": "tuple[]"270      }271    ],272    "stateMutability": "view",273    "type": "function"274  },275  {276    "inputs": [],277    "name": "collectionNestingPermissions",278    "outputs": [279      {280        "components": [281          {282            "internalType": "enum CollectionPermissionField",283            "name": "field",284            "type": "uint8"285          },286          { "internalType": "bool", "name": "value", "type": "bool" }287        ],288        "internalType": "struct CollectionNestingPermission[]",289        "name": "",290        "type": "tuple[]"291      }292    ],293    "stateMutability": "view",294    "type": "function"295  },296  {297    "inputs": [],298    "name": "collectionNestingRestrictedCollectionIds",299    "outputs": [300      {301        "components": [302          { "internalType": "bool", "name": "token_owner", "type": "bool" },303          { "internalType": "uint256[]", "name": "ids", "type": "uint256[]" }304        ],305        "internalType": "struct CollectionNesting",306        "name": "",307        "type": "tuple"308      }309    ],310    "stateMutability": "view",311    "type": "function"312  },313  {314    "inputs": [],315    "name": "collectionOwner",316    "outputs": [317      {318        "components": [319          { "internalType": "address", "name": "eth", "type": "address" },320          { "internalType": "uint256", "name": "sub", "type": "uint256" }321        ],322        "internalType": "struct CrossAddress",323        "name": "",324        "type": "tuple"325      }326    ],327    "stateMutability": "view",328    "type": "function"329  },330  {331    "inputs": [332      { "internalType": "string[]", "name": "keys", "type": "string[]" }333    ],334    "name": "collectionProperties",335    "outputs": [336      {337        "components": [338          { "internalType": "string", "name": "key", "type": "string" },339          { "internalType": "bytes", "name": "value", "type": "bytes" }340        ],341        "internalType": "struct Property[]",342        "name": "",343        "type": "tuple[]"344      }345    ],346    "stateMutability": "view",347    "type": "function"348  },349  {350    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],351    "name": "collectionProperty",352    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],353    "stateMutability": "view",354    "type": "function"355  },356  {357    "inputs": [],358    "name": "collectionSponsor",359    "outputs": [360      {361        "components": [362          { "internalType": "address", "name": "eth", "type": "address" },363          { "internalType": "uint256", "name": "sub", "type": "uint256" }364        ],365        "internalType": "struct CrossAddress",366        "name": "",367        "type": "tuple"368      }369    ],370    "stateMutability": "view",371    "type": "function"372  },373  {374    "inputs": [],375    "name": "confirmCollectionSponsorship",376    "outputs": [],377    "stateMutability": "nonpayable",378    "type": "function"379  },380  {381    "inputs": [],382    "name": "contractAddress",383    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],384    "stateMutability": "view",385    "type": "function"386  },387  {388    "inputs": [389      {390        "components": [391          { "internalType": "address", "name": "eth", "type": "address" },392          { "internalType": "uint256", "name": "sub", "type": "uint256" }393        ],394        "internalType": "struct CrossAddress",395        "name": "owner",396        "type": "tuple"397      }398    ],399    "name": "crossBalanceOf",400    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],401    "stateMutability": "view",402    "type": "function"403  },404  {405    "inputs": [406      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }407    ],408    "name": "crossOwnerOf",409    "outputs": [410      {411        "components": [412          { "internalType": "address", "name": "eth", "type": "address" },413          { "internalType": "uint256", "name": "sub", "type": "uint256" }414        ],415        "internalType": "struct CrossAddress",416        "name": "",417        "type": "tuple"418      }419    ],420    "stateMutability": "view",421    "type": "function"422  },423  {424    "inputs": [425      { "internalType": "string[]", "name": "keys", "type": "string[]" }426    ],427    "name": "deleteCollectionProperties",428    "outputs": [],429    "stateMutability": "nonpayable",430    "type": "function"431  },432  {433    "inputs": [434      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },435      { "internalType": "string[]", "name": "keys", "type": "string[]" }436    ],437    "name": "deleteProperties",438    "outputs": [],439    "stateMutability": "nonpayable",440    "type": "function"441  },442  {443    "inputs": [],444    "name": "description",445    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],446    "stateMutability": "view",447    "type": "function"448  },449  {450    "inputs": [451      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }452    ],453    "name": "getApproved",454    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],455    "stateMutability": "view",456    "type": "function"457  },458  {459    "inputs": [],460    "name": "hasCollectionPendingSponsor",461    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],462    "stateMutability": "view",463    "type": "function"464  },465  {466    "inputs": [467      { "internalType": "address", "name": "owner", "type": "address" },468      { "internalType": "address", "name": "operator", "type": "address" }469    ],470    "name": "isApprovedForAll",471    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],472    "stateMutability": "view",473    "type": "function"474  },475  {476    "inputs": [477      {478        "components": [479          { "internalType": "address", "name": "eth", "type": "address" },480          { "internalType": "uint256", "name": "sub", "type": "uint256" }481        ],482        "internalType": "struct CrossAddress",483        "name": "user",484        "type": "tuple"485      }486    ],487    "name": "isOwnerOrAdminCross",488    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],489    "stateMutability": "view",490    "type": "function"491  },492  {493    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],494    "name": "mint",495    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],496    "stateMutability": "nonpayable",497    "type": "function"498  },499  {500    "inputs": [501      {502        "components": [503          { "internalType": "address", "name": "eth", "type": "address" },504          { "internalType": "uint256", "name": "sub", "type": "uint256" }505        ],506        "internalType": "struct CrossAddress",507        "name": "to",508        "type": "tuple"509      },510      {511        "components": [512          { "internalType": "string", "name": "key", "type": "string" },513          { "internalType": "bytes", "name": "value", "type": "bytes" }514        ],515        "internalType": "struct Property[]",516        "name": "properties",517        "type": "tuple[]"518      }519    ],520    "name": "mintCross",521    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],522    "stateMutability": "nonpayable",523    "type": "function"524  },525  {526    "inputs": [527      { "internalType": "address", "name": "to", "type": "address" },528      { "internalType": "string", "name": "tokenUri", "type": "string" }529    ],530    "name": "mintWithTokenURI",531    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],532    "stateMutability": "nonpayable",533    "type": "function"534  },535  {536    "inputs": [],537    "name": "name",538    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],539    "stateMutability": "view",540    "type": "function"541  },542  {543    "inputs": [],544    "name": "nextTokenId",545    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],546    "stateMutability": "view",547    "type": "function"548  },549  {550    "inputs": [551      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }552    ],553    "name": "ownerOf",554    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],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/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 0x323b1db8
 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,
@@ -557,6 +557,15 @@
 	///  or in textual repr: crossOwnerOf(uint256)
 	function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
 
+	/// @notice Count all NFTs assigned to an owner
+	/// @dev NFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @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: 0x24e52cea,
+	///  or in textual repr: crossBalanceOf((address,uint256))
+	function crossBalanceOf(CrossAddress memory owner) external view returns (uint256);
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
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 0x9780edce
 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,
@@ -557,6 +557,15 @@
 	///  or in textual repr: crossOwnerOf(uint256)
 	function crossOwnerOf(uint256 tokenId) external view returns (CrossAddress memory);
 
+	/// @notice Count all RFTs assigned to an owner
+	/// @dev RFTs assigned to the zero address are considered invalid, and this
+	///  function throws for queries about the zero address.
+	/// @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: 0x24e52cea,
+	///  or in textual repr: crossBalanceOf((address,uint256))
+	function crossBalanceOf(CrossAddress memory owner) external view returns (uint256);
+
 	/// Returns the token properties.
 	///
 	/// @param tokenId Id for the token.
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 crossBalanceOf()', 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(BigInt(await collectionEvm.methods.crossBalanceOf(owner).call({from: owner.eth})) === 0n).to.be.true;
+
+    for (let i = 1n; i < 100n; i++) {
+      await collection.mintToken(minter, {Ethereum: owner.eth});
+      expect(BigInt(await collectionEvm.methods.crossBalanceOf(owner).call({from: owner.eth})) === i).to.be.true;
+    }
+  });
+
+  itEth('Check crossOwnerOf()', 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 < 100n; i++) {
+      const ownerCross = await collectionEvm.methods.crossOwnerOf(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 crossBalanceOf()', 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(BigInt(await collectionEvm.methods.crossBalanceOf(owner).call({from: owner.eth})) === 0n).to.be.true;
+
+    for (let i = 1n; i < 100n; i++) {
+      await collection.mintToken(minter, 100n, {Ethereum: owner.eth});
+      expect(BigInt(await collectionEvm.methods.crossBalanceOf(owner).call({from: owner.eth})) === i).to.be.true;
+    }
+  });
+
+  itEth.only('Check crossOwnerOf()', 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 < 100n; i++) {
+      const ownerCross = await collectionEvm.methods.crossOwnerOf(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.crossOwnerOf(tokenId).call({from: owner.eth});
+    expect(ownerCross.eth.toUpperCase()).to.be.eq('0XFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF');
+    expect(ownerCross.sub).to.be.eq('0');
+  });
 });
 
 describe('RFT: Fees', () => {