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
before · tests/src/eth/abi/nonFungible.json
1[2  {3    "anonymous": false,4    "inputs": [5      {6        "indexed": true,7        "internalType": "address",8        "name": "owner",9        "type": "address"10      },11      {12        "indexed": true,13        "internalType": "address",14        "name": "approved",15        "type": "address"16      },17      {18        "indexed": true,19        "internalType": "uint256",20        "name": "tokenId",21        "type": "uint256"22      }23    ],24    "name": "Approval",25    "type": "event"26  },27  {28    "anonymous": false,29    "inputs": [30      {31        "indexed": true,32        "internalType": "address",33        "name": "owner",34        "type": "address"35      },36      {37        "indexed": true,38        "internalType": "address",39        "name": "operator",40        "type": "address"41      },42      {43        "indexed": false,44        "internalType": "bool",45        "name": "approved",46        "type": "bool"47      }48    ],49    "name": "ApprovalForAll",50    "type": "event"51  },52  {53    "anonymous": false,54    "inputs": [55      {56        "indexed": true,57        "internalType": "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      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }390    ],391    "name": "crossOwnerOf",392    "outputs": [393      {394        "components": [395          { "internalType": "address", "name": "eth", "type": "address" },396          { "internalType": "uint256", "name": "sub", "type": "uint256" }397        ],398        "internalType": "struct CrossAddress",399        "name": "",400        "type": "tuple"401      }402    ],403    "stateMutability": "view",404    "type": "function"405  },406  {407    "inputs": [408      { "internalType": "string[]", "name": "keys", "type": "string[]" }409    ],410    "name": "deleteCollectionProperties",411    "outputs": [],412    "stateMutability": "nonpayable",413    "type": "function"414  },415  {416    "inputs": [417      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },418      { "internalType": "string[]", "name": "keys", "type": "string[]" }419    ],420    "name": "deleteProperties",421    "outputs": [],422    "stateMutability": "nonpayable",423    "type": "function"424  },425  {426    "inputs": [],427    "name": "description",428    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],429    "stateMutability": "view",430    "type": "function"431  },432  {433    "inputs": [434      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }435    ],436    "name": "getApproved",437    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],438    "stateMutability": "view",439    "type": "function"440  },441  {442    "inputs": [],443    "name": "hasCollectionPendingSponsor",444    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],445    "stateMutability": "view",446    "type": "function"447  },448  {449    "inputs": [450      { "internalType": "address", "name": "owner", "type": "address" },451      { "internalType": "address", "name": "operator", "type": "address" }452    ],453    "name": "isApprovedForAll",454    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],455    "stateMutability": "view",456    "type": "function"457  },458  {459    "inputs": [460      {461        "components": [462          { "internalType": "address", "name": "eth", "type": "address" },463          { "internalType": "uint256", "name": "sub", "type": "uint256" }464        ],465        "internalType": "struct CrossAddress",466        "name": "user",467        "type": "tuple"468      }469    ],470    "name": "isOwnerOrAdminCross",471    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],472    "stateMutability": "view",473    "type": "function"474  },475  {476    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],477    "name": "mint",478    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],479    "stateMutability": "nonpayable",480    "type": "function"481  },482  {483    "inputs": [484      {485        "components": [486          { "internalType": "address", "name": "eth", "type": "address" },487          { "internalType": "uint256", "name": "sub", "type": "uint256" }488        ],489        "internalType": "struct CrossAddress",490        "name": "to",491        "type": "tuple"492      },493      {494        "components": [495          { "internalType": "string", "name": "key", "type": "string" },496          { "internalType": "bytes", "name": "value", "type": "bytes" }497        ],498        "internalType": "struct Property[]",499        "name": "properties",500        "type": "tuple[]"501      }502    ],503    "name": "mintCross",504    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],505    "stateMutability": "nonpayable",506    "type": "function"507  },508  {509    "inputs": [510      { "internalType": "address", "name": "to", "type": "address" },511      { "internalType": "string", "name": "tokenUri", "type": "string" }512    ],513    "name": "mintWithTokenURI",514    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],515    "stateMutability": "nonpayable",516    "type": "function"517  },518  {519    "inputs": [],520    "name": "name",521    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],522    "stateMutability": "view",523    "type": "function"524  },525  {526    "inputs": [],527    "name": "nextTokenId",528    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],529    "stateMutability": "view",530    "type": "function"531  },532  {533    "inputs": [534      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }535    ],536    "name": "ownerOf",537    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],538    "stateMutability": "view",539    "type": "function"540  },541  {542    "inputs": [543      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },544      { "internalType": "string[]", "name": "keys", "type": "string[]" }545    ],546    "name": "properties",547    "outputs": [548      {549        "components": [550          { "internalType": "string", "name": "key", "type": "string" },551          { "internalType": "bytes", "name": "value", "type": "bytes" }552        ],553        "internalType": "struct Property[]",554        "name": "",555        "type": "tuple[]"556      }557    ],558    "stateMutability": "view",559    "type": "function"560  },561  {562    "inputs": [563      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },564      { "internalType": "string", "name": "key", "type": "string" }565    ],566    "name": "property",567    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],568    "stateMutability": "view",569    "type": "function"570  },571  {572    "inputs": [573      {574        "components": [575          { "internalType": "address", "name": "eth", "type": "address" },576          { "internalType": "uint256", "name": "sub", "type": "uint256" }577        ],578        "internalType": "struct CrossAddress",579        "name": "admin",580        "type": "tuple"581      }582    ],583    "name": "removeCollectionAdminCross",584    "outputs": [],585    "stateMutability": "nonpayable",586    "type": "function"587  },588  {589    "inputs": [],590    "name": "removeCollectionSponsor",591    "outputs": [],592    "stateMutability": "nonpayable",593    "type": "function"594  },595  {596    "inputs": [597      {598        "components": [599          { "internalType": "address", "name": "eth", "type": "address" },600          { "internalType": "uint256", "name": "sub", "type": "uint256" }601        ],602        "internalType": "struct CrossAddress",603        "name": "user",604        "type": "tuple"605      }606    ],607    "name": "removeFromCollectionAllowListCross",608    "outputs": [],609    "stateMutability": "nonpayable",610    "type": "function"611  },612  {613    "inputs": [614      { "internalType": "address", "name": "from", "type": "address" },615      { "internalType": "address", "name": "to", "type": "address" },616      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }617    ],618    "name": "safeTransferFrom",619    "outputs": [],620    "stateMutability": "nonpayable",621    "type": "function"622  },623  {624    "inputs": [625      { "internalType": "address", "name": "from", "type": "address" },626      { "internalType": "address", "name": "to", "type": "address" },627      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },628      { "internalType": "bytes", "name": "data", "type": "bytes" }629    ],630    "name": "safeTransferFrom",631    "outputs": [],632    "stateMutability": "nonpayable",633    "type": "function"634  },635  {636    "inputs": [637      { "internalType": "address", "name": "operator", "type": "address" },638      { "internalType": "bool", "name": "approved", "type": "bool" }639    ],640    "name": "setApprovalForAll",641    "outputs": [],642    "stateMutability": "nonpayable",643    "type": "function"644  },645  {646    "inputs": [647      { "internalType": "enum AccessMode", "name": "mode", "type": "uint8" }648    ],649    "name": "setCollectionAccess",650    "outputs": [],651    "stateMutability": "nonpayable",652    "type": "function"653  },654  {655    "inputs": [656      {657        "components": [658          {659            "internalType": "enum CollectionLimitField",660            "name": "field",661            "type": "uint8"662          },663          {664            "components": [665              { "internalType": "bool", "name": "status", "type": "bool" },666              { "internalType": "uint256", "name": "value", "type": "uint256" }667            ],668            "internalType": "struct OptionUint256",669            "name": "value",670            "type": "tuple"671          }672        ],673        "internalType": "struct CollectionLimit",674        "name": "limit",675        "type": "tuple"676      }677    ],678    "name": "setCollectionLimit",679    "outputs": [],680    "stateMutability": "nonpayable",681    "type": "function"682  },683  {684    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],685    "name": "setCollectionMintMode",686    "outputs": [],687    "stateMutability": "nonpayable",688    "type": "function"689  },690  {691    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],692    "name": "setCollectionNesting",693    "outputs": [],694    "stateMutability": "nonpayable",695    "type": "function"696  },697  {698    "inputs": [699      { "internalType": "bool", "name": "enable", "type": "bool" },700      {701        "internalType": "address[]",702        "name": "collections",703        "type": "address[]"704      }705    ],706    "name": "setCollectionNesting",707    "outputs": [],708    "stateMutability": "nonpayable",709    "type": "function"710  },711  {712    "inputs": [713      {714        "components": [715          { "internalType": "string", "name": "key", "type": "string" },716          { "internalType": "bytes", "name": "value", "type": "bytes" }717        ],718        "internalType": "struct Property[]",719        "name": "properties",720        "type": "tuple[]"721      }722    ],723    "name": "setCollectionProperties",724    "outputs": [],725    "stateMutability": "nonpayable",726    "type": "function"727  },728  {729    "inputs": [730      {731        "components": [732          { "internalType": "address", "name": "eth", "type": "address" },733          { "internalType": "uint256", "name": "sub", "type": "uint256" }734        ],735        "internalType": "struct CrossAddress",736        "name": "sponsor",737        "type": "tuple"738      }739    ],740    "name": "setCollectionSponsorCross",741    "outputs": [],742    "stateMutability": "nonpayable",743    "type": "function"744  },745  {746    "inputs": [747      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },748      {749        "components": [750          { "internalType": "string", "name": "key", "type": "string" },751          { "internalType": "bytes", "name": "value", "type": "bytes" }752        ],753        "internalType": "struct Property[]",754        "name": "properties",755        "type": "tuple[]"756      }757    ],758    "name": "setProperties",759    "outputs": [],760    "stateMutability": "nonpayable",761    "type": "function"762  },763  {764    "inputs": [765      {766        "components": [767          { "internalType": "string", "name": "key", "type": "string" },768          {769            "components": [770              {771                "internalType": "enum TokenPermissionField",772                "name": "code",773                "type": "uint8"774              },775              { "internalType": "bool", "name": "value", "type": "bool" }776            ],777            "internalType": "struct PropertyPermission[]",778            "name": "permissions",779            "type": "tuple[]"780          }781        ],782        "internalType": "struct TokenPropertyPermission[]",783        "name": "permissions",784        "type": "tuple[]"785      }786    ],787    "name": "setTokenPropertyPermissions",788    "outputs": [],789    "stateMutability": "nonpayable",790    "type": "function"791  },792  {793    "inputs": [794      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }795    ],796    "name": "supportsInterface",797    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],798    "stateMutability": "view",799    "type": "function"800  },801  {802    "inputs": [],803    "name": "symbol",804    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],805    "stateMutability": "view",806    "type": "function"807  },808  {809    "inputs": [810      { "internalType": "uint256", "name": "index", "type": "uint256" }811    ],812    "name": "tokenByIndex",813    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],814    "stateMutability": "view",815    "type": "function"816  },817  {818    "inputs": [819      { "internalType": "address", "name": "owner", "type": "address" },820      { "internalType": "uint256", "name": "index", "type": "uint256" }821    ],822    "name": "tokenOfOwnerByIndex",823    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],824    "stateMutability": "view",825    "type": "function"826  },827  {828    "inputs": [],829    "name": "tokenPropertyPermissions",830    "outputs": [831      {832        "components": [833          { "internalType": "string", "name": "key", "type": "string" },834          {835            "components": [836              {837                "internalType": "enum TokenPermissionField",838                "name": "code",839                "type": "uint8"840              },841              { "internalType": "bool", "name": "value", "type": "bool" }842            ],843            "internalType": "struct PropertyPermission[]",844            "name": "permissions",845            "type": "tuple[]"846          }847        ],848        "internalType": "struct TokenPropertyPermission[]",849        "name": "",850        "type": "tuple[]"851      }852    ],853    "stateMutability": "view",854    "type": "function"855  },856  {857    "inputs": [858      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }859    ],860    "name": "tokenURI",861    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],862    "stateMutability": "view",863    "type": "function"864  },865  {866    "inputs": [],867    "name": "totalSupply",868    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],869    "stateMutability": "view",870    "type": "function"871  },872  {873    "inputs": [874      { "internalType": "address", "name": "to", "type": "address" },875      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }876    ],877    "name": "transfer",878    "outputs": [],879    "stateMutability": "nonpayable",880    "type": "function"881  },882  {883    "inputs": [884      {885        "components": [886          { "internalType": "address", "name": "eth", "type": "address" },887          { "internalType": "uint256", "name": "sub", "type": "uint256" }888        ],889        "internalType": "struct CrossAddress",890        "name": "to",891        "type": "tuple"892      },893      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }894    ],895    "name": "transferCross",896    "outputs": [],897    "stateMutability": "nonpayable",898    "type": "function"899  },900  {901    "inputs": [902      { "internalType": "address", "name": "from", "type": "address" },903      { "internalType": "address", "name": "to", "type": "address" },904      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }905    ],906    "name": "transferFrom",907    "outputs": [],908    "stateMutability": "nonpayable",909    "type": "function"910  },911  {912    "inputs": [913      {914        "components": [915          { "internalType": "address", "name": "eth", "type": "address" },916          { "internalType": "uint256", "name": "sub", "type": "uint256" }917        ],918        "internalType": "struct CrossAddress",919        "name": "from",920        "type": "tuple"921      },922      {923        "components": [924          { "internalType": "address", "name": "eth", "type": "address" },925          { "internalType": "uint256", "name": "sub", "type": "uint256" }926        ],927        "internalType": "struct CrossAddress",928        "name": "to",929        "type": "tuple"930      },931      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }932    ],933    "name": "transferFromCross",934    "outputs": [],935    "stateMutability": "nonpayable",936    "type": "function"937  },938  {939    "inputs": [],940    "name": "uniqueCollectionType",941    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],942    "stateMutability": "view",943    "type": "function"944  }945]
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', () => {