git.delta.rocks / unique-network / refs/commits / 36fc5abfd9d0

difftreelog

feat add delete properties

Trubnikov Sergey2022-10-24parent: #ec90529.patch.diff
in: master

17 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -117,8 +117,6 @@
 	/// @param key Property key.
 	#[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]
 	fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {
-		self.consume_store_reads_and_writes(1, 1)?;
-
 		let caller = T::CrossAccountId::from_eth(caller);
 		let key = <Vec<u8>>::from(key)
 			.try_into()
@@ -127,6 +125,24 @@
 		<Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)
 	}
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	#[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]
+	fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		let keys = keys
+			.into_iter()
+			.map(|key| {
+				<Vec<u8>>::from(key)
+					.try_into()
+					.map_err(|_| Error::Revert("key too large".into()))
+			})
+			.collect::<Result<Vec<_>>>()?;
+
+		<Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)
+	}
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -146,28 +162,34 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {
-		let mut keys_ = Vec::<PropertyKey>::with_capacity(keys.len());
-		for key in keys {
-			keys_.push(
+		let keys = keys
+			.into_iter()
+			.map(|key| {
 				<Vec<u8>>::from(key)
 					.try_into()
-					.map_err(|_| Error::Revert("key too large".into()))?,
-			)
-		}
-		let properties = Pallet::<T>::filter_collection_properties(self.id, Some(keys_))
-			.map_err(dispatch_to_evm::<T>)?;
+					.map_err(|_| Error::Revert("key too large".into()))
+			})
+			.collect::<Result<Vec<_>>>()?;
 
-		let mut properties_ = Vec::<(string, bytes)>::with_capacity(properties.len());
-		for p in properties {
-			let key =
-				string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;
-			let value = bytes(p.value.to_vec());
-			properties_.push((key, value));
-		}
-		Ok(properties_)
+		let properties = Pallet::<T>::filter_collection_properties(
+			self.id,
+			if keys.is_empty() { None } else { Some(keys) },
+		)
+		.map_err(dispatch_to_evm::<T>)?;
+
+		let properties = properties
+			.into_iter()
+			.map(|p| {
+				let key =
+					string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;
+				let value = bytes(p.value.to_vec());
+				Ok((key, value))
+			})
+			.collect::<Result<Vec<_>>>()?;
+		Ok(properties)
 	}
 
 	/// Set the sponsor of the collection.
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -18,7 +18,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -55,6 +55,17 @@
 		dummy = 0;
 	}
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) public {
+		require(false, stub_error);
+		keys;
+		dummy = 0;
+	}
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -72,7 +83,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
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
@@ -91,7 +91,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -128,6 +128,17 @@
 		dummy = 0;
 	}
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) public {
+		require(false, stub_error);
+		keys;
+		dummy = 0;
+	}
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -145,7 +156,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -91,7 +91,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -128,6 +128,17 @@
 		dummy = 0;
 	}
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) public {
+		require(false, stub_error);
+		keys;
+		dummy = 0;
+	}
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -145,7 +156,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -37,6 +37,13 @@
 	///  or in textual repr: deleteCollectionProperty(string)
 	function deleteCollectionProperty(string memory key) external;
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -49,7 +56,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -62,7 +62,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -86,6 +86,13 @@
 	///  or in textual repr: deleteCollectionProperty(string)
 	function deleteCollectionProperty(string memory key) external;
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -98,7 +105,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -62,7 +62,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0x5d354410
+/// @dev the ERC-165 identifier for this interface is 0xb3152af3
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -86,6 +86,13 @@
 	///  or in textual repr: deleteCollectionProperty(string)
 	function deleteCollectionProperty(string memory key) external;
 
+	/// Delete collection properties.
+	///
+	/// @param keys Properties keys.
+	/// @dev EVM selector for this function is: 0xee206ee3,
+	///  or in textual repr: deleteCollectionProperties(string[])
+	function deleteCollectionProperties(string[] memory keys) external;
+
 	/// Get collection property.
 	///
 	/// @dev Throws error if key not found.
@@ -98,7 +105,7 @@
 
 	/// Get collection properties.
 	///
-	/// @param keys Properties keys.
+	/// @param keys Properties keys. Empty keys for all propertyes.
 	/// @return Vector of properties key/value pairs.
 	/// @dev EVM selector for this function is: 0x285fb8e6,
 	///  or in textual repr: collectionProperties(string[])
modifiedtests/src/eth/collectionProperties.test.tsdiffbeforeafterboth
--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -16,7 +16,7 @@
 
 import {itEth, usingEthPlaygrounds, expect, EthUniqueHelper} from './util';
 import {Pallets} from '../util';
-import {IProperty, ITokenPropertyPermission} from '../util/playgrounds/types';
+import {IProperty, ITokenPropertyPermission, TCollectionMode} from '../util/playgrounds/types';
 import {IKeyringPair} from '@polkadot/types/types';
 
 describe('EVM collection properties', () => {
@@ -163,29 +163,89 @@
 });
 
 describe('EVM collection property', () => {
-  itEth('Set/read properties', async ({helper, privateKey}) => {
-    const alice = await privateKey('//Alice');
-    const collection = await helper.nft.mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+  let alice: IKeyringPair;
+
+  before(() => {
+    usingEthPlaygrounds(async (_helper, privateKey) => {
+      alice = await privateKey('//Alice');
+    });
+  });
+
+  async function testSetReadProperties(helper: EthUniqueHelper, mode: TCollectionMode) {
+    const collection = await helper[mode].mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
 
     const sender = await helper.eth.createAccountWithBalance(alice, 100n);
     await collection.addAdmin(alice, {Ethereum: sender});
 
     const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
-    const contract = helper.ethNativeContract.collection(collectionAddress, 'nft', sender);
+    const contract = helper.ethNativeContract.collection(collectionAddress, mode, sender);
 
-    const key1 = 'key1';
-    const value1 = Buffer.from('value1');
-    
-    const key2 = 'key2';
-    const value2 = Buffer.from('value2');
+    const keys = ['key0', 'key1'];
 
     const writeProperties = [
-      [key1, '0x'+value1.toString('hex')],
-      [key2, '0x'+value2.toString('hex')],
+      helper.ethProperty.property(keys[0], 'value0'),
+      helper.ethProperty.property(keys[1], 'value1'),
     ];
 
     await contract.methods.setCollectionProperties(writeProperties).send();
-    const readProperties = await contract.methods.collectionProperties([key1, key2]).call();
+    const readProperties = await contract.methods.collectionProperties([keys[0], keys[1]]).call();
     expect(readProperties).to.be.like(writeProperties);
+  }
+
+  itEth('Set/read properties ft', async ({helper}) => {
+    await testSetReadProperties(helper, 'ft');
+  });
+  itEth('Set/read properties rft', async ({helper}) => {
+    await testSetReadProperties(helper, 'rft');
+  });
+  itEth('Set/read properties nft', async ({helper}) => {
+    await testSetReadProperties(helper, 'nft');
+  });
+
+  async function testDeleteProperties(helper: EthUniqueHelper, mode: TCollectionMode) {
+    const collection = await helper[mode].mintCollection(alice, {name: 'A', description: 'B', tokenPrefix: 'C'});
+
+    const sender = await helper.eth.createAccountWithBalance(alice, 100n);
+    await collection.addAdmin(alice, {Ethereum: sender});
+
+    const collectionAddress = helper.ethAddress.fromCollectionId(collection.collectionId);
+    const contract = helper.ethNativeContract.collection(collectionAddress, mode, sender);
+
+    const keys = ['key0', 'key1', 'key2', 'key3'];
+
+    {
+      const writeProperties = [
+        helper.ethProperty.property(keys[0], 'value0'),
+        helper.ethProperty.property(keys[1], 'value1'),
+        helper.ethProperty.property(keys[2], 'value2'),
+        helper.ethProperty.property(keys[3], 'value3'),
+      ];
+
+      await contract.methods.setCollectionProperties(writeProperties).send();
+      const readProperties = await contract.methods.collectionProperties([keys[0], keys[1], keys[2], keys[3]]).call();
+      expect(readProperties).to.be.like(writeProperties);
+    }
+
+    {
+      const expectProperties = [
+        helper.ethProperty.property(keys[0], 'value0'),
+        helper.ethProperty.property(keys[1], 'value1'),
+      ];
+
+      await contract.methods.deleteCollectionProperties([keys[2], keys[3]]).send();
+      const readProperties = await contract.methods.collectionProperties([]).call();
+      expect(readProperties).to.be.like(expectProperties);
+    }
+  }
+  
+  itEth('Delete properties ft', async ({helper}) => {
+    await testDeleteProperties(helper, 'ft');
+  });
+  itEth('Delete properties rft', async ({helper}) => {
+    await testDeleteProperties(helper, 'rft');
   });
+  itEth('Delete properties nft', async ({helper}) => {
+    await testDeleteProperties(helper, 'nft');
+  });
+    
 });
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -293,6 +293,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "deleteCollectionProperty",
     "outputs": [],
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -316,6 +316,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "string[]", "name": "keys", "type": "string[]" }
+    ],
+    "name": "deleteCollectionProperties",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "deleteCollectionProperty",
     "outputs": [],
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
before · tests/src/eth/reFungibleAbi.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    "name": "MintingFinished",56    "type": "event"57  },58  {59    "anonymous": false,60    "inputs": [61      {62        "indexed": true,63        "internalType": "address",64        "name": "from",65        "type": "address"66      },67      {68        "indexed": true,69        "internalType": "address",70        "name": "to",71        "type": "address"72      },73      {74        "indexed": true,75        "internalType": "uint256",76        "name": "tokenId",77        "type": "uint256"78      }79    ],80    "name": "Transfer",81    "type": "event"82  },83  {84    "inputs": [85      { "internalType": "address", "name": "newAdmin", "type": "address" }86    ],87    "name": "addCollectionAdmin",88    "outputs": [],89    "stateMutability": "nonpayable",90    "type": "function"91  },92  {93    "inputs": [94      {95        "components": [96          { "internalType": "address", "name": "field_0", "type": "address" },97          { "internalType": "uint256", "name": "field_1", "type": "uint256" }98        ],99        "internalType": "struct Tuple6",100        "name": "newAdmin",101        "type": "tuple"102      }103    ],104    "name": "addCollectionAdminCross",105    "outputs": [],106    "stateMutability": "nonpayable",107    "type": "function"108  },109  {110    "inputs": [111      { "internalType": "address", "name": "user", "type": "address" }112    ],113    "name": "addToCollectionAllowList",114    "outputs": [],115    "stateMutability": "nonpayable",116    "type": "function"117  },118  {119    "inputs": [120      {121        "components": [122          { "internalType": "address", "name": "field_0", "type": "address" },123          { "internalType": "uint256", "name": "field_1", "type": "uint256" }124        ],125        "internalType": "struct Tuple6",126        "name": "user",127        "type": "tuple"128      }129    ],130    "name": "addToCollectionAllowListCross",131    "outputs": [],132    "stateMutability": "nonpayable",133    "type": "function"134  },135  {136    "inputs": [137      { "internalType": "address", "name": "user", "type": "address" }138    ],139    "name": "allowed",140    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],141    "stateMutability": "view",142    "type": "function"143  },144  {145    "inputs": [146      { "internalType": "address", "name": "approved", "type": "address" },147      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }148    ],149    "name": "approve",150    "outputs": [],151    "stateMutability": "nonpayable",152    "type": "function"153  },154  {155    "inputs": [156      { "internalType": "address", "name": "owner", "type": "address" }157    ],158    "name": "balanceOf",159    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],160    "stateMutability": "view",161    "type": "function"162  },163  {164    "inputs": [165      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }166    ],167    "name": "burn",168    "outputs": [],169    "stateMutability": "nonpayable",170    "type": "function"171  },172  {173    "inputs": [174      { "internalType": "address", "name": "from", "type": "address" },175      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }176    ],177    "name": "burnFrom",178    "outputs": [],179    "stateMutability": "nonpayable",180    "type": "function"181  },182  {183    "inputs": [184      {185        "components": [186          { "internalType": "address", "name": "field_0", "type": "address" },187          { "internalType": "uint256", "name": "field_1", "type": "uint256" }188        ],189        "internalType": "struct Tuple6",190        "name": "from",191        "type": "tuple"192      },193      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }194    ],195    "name": "burnFromCross",196    "outputs": [],197    "stateMutability": "nonpayable",198    "type": "function"199  },200  {201    "inputs": [202      { "internalType": "address", "name": "newOwner", "type": "address" }203    ],204    "name": "changeCollectionOwner",205    "outputs": [],206    "stateMutability": "nonpayable",207    "type": "function"208  },209  {210    "inputs": [],211    "name": "collectionAdmins",212    "outputs": [213      {214        "components": [215          { "internalType": "address", "name": "field_0", "type": "address" },216          { "internalType": "uint256", "name": "field_1", "type": "uint256" }217        ],218        "internalType": "struct Tuple6[]",219        "name": "",220        "type": "tuple[]"221      }222    ],223    "stateMutability": "view",224    "type": "function"225  },226  {227    "inputs": [],228    "name": "collectionOwner",229    "outputs": [230      {231        "components": [232          { "internalType": "address", "name": "field_0", "type": "address" },233          { "internalType": "uint256", "name": "field_1", "type": "uint256" }234        ],235        "internalType": "struct Tuple6",236        "name": "",237        "type": "tuple"238      }239    ],240    "stateMutability": "view",241    "type": "function"242  },243  {244    "inputs": [245      { "internalType": "string[]", "name": "keys", "type": "string[]" }246    ],247    "name": "collectionProperties",248    "outputs": [249      {250        "components": [251          { "internalType": "string", "name": "field_0", "type": "string" },252          { "internalType": "bytes", "name": "field_1", "type": "bytes" }253        ],254        "internalType": "struct Tuple19[]",255        "name": "",256        "type": "tuple[]"257      }258    ],259    "stateMutability": "view",260    "type": "function"261  },262  {263    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],264    "name": "collectionProperty",265    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],266    "stateMutability": "view",267    "type": "function"268  },269  {270    "inputs": [],271    "name": "collectionSponsor",272    "outputs": [273      {274        "components": [275          { "internalType": "address", "name": "field_0", "type": "address" },276          { "internalType": "uint256", "name": "field_1", "type": "uint256" }277        ],278        "internalType": "struct Tuple6",279        "name": "",280        "type": "tuple"281      }282    ],283    "stateMutability": "view",284    "type": "function"285  },286  {287    "inputs": [],288    "name": "confirmCollectionSponsorship",289    "outputs": [],290    "stateMutability": "nonpayable",291    "type": "function"292  },293  {294    "inputs": [],295    "name": "contractAddress",296    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],297    "stateMutability": "view",298    "type": "function"299  },300  {301    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],302    "name": "deleteCollectionProperty",303    "outputs": [],304    "stateMutability": "nonpayable",305    "type": "function"306  },307  {308    "inputs": [309      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },310      { "internalType": "string", "name": "key", "type": "string" }311    ],312    "name": "deleteProperty",313    "outputs": [],314    "stateMutability": "nonpayable",315    "type": "function"316  },317  {318    "inputs": [],319    "name": "finishMinting",320    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],321    "stateMutability": "nonpayable",322    "type": "function"323  },324  {325    "inputs": [326      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }327    ],328    "name": "getApproved",329    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],330    "stateMutability": "view",331    "type": "function"332  },333  {334    "inputs": [],335    "name": "hasCollectionPendingSponsor",336    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],337    "stateMutability": "view",338    "type": "function"339  },340  {341    "inputs": [342      { "internalType": "address", "name": "owner", "type": "address" },343      { "internalType": "address", "name": "operator", "type": "address" }344    ],345    "name": "isApprovedForAll",346    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],347    "stateMutability": "view",348    "type": "function"349  },350  {351    "inputs": [352      { "internalType": "address", "name": "user", "type": "address" }353    ],354    "name": "isOwnerOrAdmin",355    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],356    "stateMutability": "view",357    "type": "function"358  },359  {360    "inputs": [361      {362        "components": [363          { "internalType": "address", "name": "field_0", "type": "address" },364          { "internalType": "uint256", "name": "field_1", "type": "uint256" }365        ],366        "internalType": "struct Tuple6",367        "name": "user",368        "type": "tuple"369      }370    ],371    "name": "isOwnerOrAdminCross",372    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],373    "stateMutability": "view",374    "type": "function"375  },376  {377    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],378    "name": "mint",379    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],380    "stateMutability": "nonpayable",381    "type": "function"382  },383  {384    "inputs": [385      { "internalType": "address", "name": "to", "type": "address" },386      { "internalType": "string", "name": "tokenUri", "type": "string" }387    ],388    "name": "mintWithTokenURI",389    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],390    "stateMutability": "nonpayable",391    "type": "function"392  },393  {394    "inputs": [],395    "name": "mintingFinished",396    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],397    "stateMutability": "view",398    "type": "function"399  },400  {401    "inputs": [],402    "name": "name",403    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],404    "stateMutability": "view",405    "type": "function"406  },407  {408    "inputs": [],409    "name": "nextTokenId",410    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],411    "stateMutability": "view",412    "type": "function"413  },414  {415    "inputs": [416      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }417    ],418    "name": "ownerOf",419    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],420    "stateMutability": "view",421    "type": "function"422  },423  {424    "inputs": [425      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },426      { "internalType": "string", "name": "key", "type": "string" }427    ],428    "name": "property",429    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],430    "stateMutability": "view",431    "type": "function"432  },433  {434    "inputs": [435      { "internalType": "address", "name": "admin", "type": "address" }436    ],437    "name": "removeCollectionAdmin",438    "outputs": [],439    "stateMutability": "nonpayable",440    "type": "function"441  },442  {443    "inputs": [444      {445        "components": [446          { "internalType": "address", "name": "field_0", "type": "address" },447          { "internalType": "uint256", "name": "field_1", "type": "uint256" }448        ],449        "internalType": "struct Tuple6",450        "name": "admin",451        "type": "tuple"452      }453    ],454    "name": "removeCollectionAdminCross",455    "outputs": [],456    "stateMutability": "nonpayable",457    "type": "function"458  },459  {460    "inputs": [],461    "name": "removeCollectionSponsor",462    "outputs": [],463    "stateMutability": "nonpayable",464    "type": "function"465  },466  {467    "inputs": [468      { "internalType": "address", "name": "user", "type": "address" }469    ],470    "name": "removeFromCollectionAllowList",471    "outputs": [],472    "stateMutability": "nonpayable",473    "type": "function"474  },475  {476    "inputs": [477      {478        "components": [479          { "internalType": "address", "name": "field_0", "type": "address" },480          { "internalType": "uint256", "name": "field_1", "type": "uint256" }481        ],482        "internalType": "struct Tuple6",483        "name": "user",484        "type": "tuple"485      }486    ],487    "name": "removeFromCollectionAllowListCross",488    "outputs": [],489    "stateMutability": "nonpayable",490    "type": "function"491  },492  {493    "inputs": [494      { "internalType": "address", "name": "from", "type": "address" },495      { "internalType": "address", "name": "to", "type": "address" },496      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }497    ],498    "name": "safeTransferFrom",499    "outputs": [],500    "stateMutability": "nonpayable",501    "type": "function"502  },503  {504    "inputs": [505      { "internalType": "address", "name": "from", "type": "address" },506      { "internalType": "address", "name": "to", "type": "address" },507      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },508      { "internalType": "bytes", "name": "data", "type": "bytes" }509    ],510    "name": "safeTransferFromWithData",511    "outputs": [],512    "stateMutability": "nonpayable",513    "type": "function"514  },515  {516    "inputs": [517      { "internalType": "address", "name": "operator", "type": "address" },518      { "internalType": "bool", "name": "approved", "type": "bool" }519    ],520    "name": "setApprovalForAll",521    "outputs": [],522    "stateMutability": "nonpayable",523    "type": "function"524  },525  {526    "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],527    "name": "setCollectionAccess",528    "outputs": [],529    "stateMutability": "nonpayable",530    "type": "function"531  },532  {533    "inputs": [534      { "internalType": "string", "name": "limit", "type": "string" },535      { "internalType": "uint32", "name": "value", "type": "uint32" }536    ],537    "name": "setCollectionLimit",538    "outputs": [],539    "stateMutability": "nonpayable",540    "type": "function"541  },542  {543    "inputs": [544      { "internalType": "string", "name": "limit", "type": "string" },545      { "internalType": "bool", "name": "value", "type": "bool" }546    ],547    "name": "setCollectionLimit",548    "outputs": [],549    "stateMutability": "nonpayable",550    "type": "function"551  },552  {553    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],554    "name": "setCollectionMintMode",555    "outputs": [],556    "stateMutability": "nonpayable",557    "type": "function"558  },559  {560    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],561    "name": "setCollectionNesting",562    "outputs": [],563    "stateMutability": "nonpayable",564    "type": "function"565  },566  {567    "inputs": [568      { "internalType": "bool", "name": "enable", "type": "bool" },569      {570        "internalType": "address[]",571        "name": "collections",572        "type": "address[]"573      }574    ],575    "name": "setCollectionNesting",576    "outputs": [],577    "stateMutability": "nonpayable",578    "type": "function"579  },580  {581    "inputs": [582      {583        "components": [584          { "internalType": "string", "name": "field_0", "type": "string" },585          { "internalType": "bytes", "name": "field_1", "type": "bytes" }586        ],587        "internalType": "struct Tuple19[]",588        "name": "properties",589        "type": "tuple[]"590      }591    ],592    "name": "setCollectionProperties",593    "outputs": [],594    "stateMutability": "nonpayable",595    "type": "function"596  },597  {598    "inputs": [599      { "internalType": "string", "name": "key", "type": "string" },600      { "internalType": "bytes", "name": "value", "type": "bytes" }601    ],602    "name": "setCollectionProperty",603    "outputs": [],604    "stateMutability": "nonpayable",605    "type": "function"606  },607  {608    "inputs": [609      { "internalType": "address", "name": "sponsor", "type": "address" }610    ],611    "name": "setCollectionSponsor",612    "outputs": [],613    "stateMutability": "nonpayable",614    "type": "function"615  },616  {617    "inputs": [618      {619        "components": [620          { "internalType": "address", "name": "field_0", "type": "address" },621          { "internalType": "uint256", "name": "field_1", "type": "uint256" }622        ],623        "internalType": "struct Tuple6",624        "name": "sponsor",625        "type": "tuple"626      }627    ],628    "name": "setCollectionSponsorCross",629    "outputs": [],630    "stateMutability": "nonpayable",631    "type": "function"632  },633  {634    "inputs": [635      {636        "components": [637          { "internalType": "address", "name": "field_0", "type": "address" },638          { "internalType": "uint256", "name": "field_1", "type": "uint256" }639        ],640        "internalType": "struct Tuple6",641        "name": "newOwner",642        "type": "tuple"643      }644    ],645    "name": "setOwnerCross",646    "outputs": [],647    "stateMutability": "nonpayable",648    "type": "function"649  },650  {651    "inputs": [652      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },653      { "internalType": "string", "name": "key", "type": "string" },654      { "internalType": "bytes", "name": "value", "type": "bytes" }655    ],656    "name": "setProperty",657    "outputs": [],658    "stateMutability": "nonpayable",659    "type": "function"660  },661  {662    "inputs": [663      { "internalType": "string", "name": "key", "type": "string" },664      { "internalType": "bool", "name": "isMutable", "type": "bool" },665      { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },666      { "internalType": "bool", "name": "tokenOwner", "type": "bool" }667    ],668    "name": "setTokenPropertyPermission",669    "outputs": [],670    "stateMutability": "nonpayable",671    "type": "function"672  },673  {674    "inputs": [675      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }676    ],677    "name": "supportsInterface",678    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],679    "stateMutability": "view",680    "type": "function"681  },682  {683    "inputs": [],684    "name": "symbol",685    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],686    "stateMutability": "view",687    "type": "function"688  },689  {690    "inputs": [691      { "internalType": "uint256", "name": "index", "type": "uint256" }692    ],693    "name": "tokenByIndex",694    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],695    "stateMutability": "view",696    "type": "function"697  },698  {699    "inputs": [700      { "internalType": "uint256", "name": "token", "type": "uint256" }701    ],702    "name": "tokenContractAddress",703    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],704    "stateMutability": "view",705    "type": "function"706  },707  {708    "inputs": [709      { "internalType": "address", "name": "owner", "type": "address" },710      { "internalType": "uint256", "name": "index", "type": "uint256" }711    ],712    "name": "tokenOfOwnerByIndex",713    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],714    "stateMutability": "view",715    "type": "function"716  },717  {718    "inputs": [719      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }720    ],721    "name": "tokenURI",722    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],723    "stateMutability": "view",724    "type": "function"725  },726  {727    "inputs": [],728    "name": "totalSupply",729    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],730    "stateMutability": "view",731    "type": "function"732  },733  {734    "inputs": [735      { "internalType": "address", "name": "to", "type": "address" },736      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }737    ],738    "name": "transfer",739    "outputs": [],740    "stateMutability": "nonpayable",741    "type": "function"742  },743  {744    "inputs": [745      { "internalType": "address", "name": "from", "type": "address" },746      { "internalType": "address", "name": "to", "type": "address" },747      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }748    ],749    "name": "transferFrom",750    "outputs": [],751    "stateMutability": "nonpayable",752    "type": "function"753  },754  {755    "inputs": [756      {757        "components": [758          { "internalType": "address", "name": "field_0", "type": "address" },759          { "internalType": "uint256", "name": "field_1", "type": "uint256" }760        ],761        "internalType": "struct Tuple6",762        "name": "from",763        "type": "tuple"764      },765      {766        "components": [767          { "internalType": "address", "name": "field_0", "type": "address" },768          { "internalType": "uint256", "name": "field_1", "type": "uint256" }769        ],770        "internalType": "struct Tuple6",771        "name": "to",772        "type": "tuple"773      },774      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }775    ],776    "name": "transferFromCross",777    "outputs": [],778    "stateMutability": "nonpayable",779    "type": "function"780  },781  {782    "inputs": [],783    "name": "uniqueCollectionType",784    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],785    "stateMutability": "view",786    "type": "function"787  }788]
after · tests/src/eth/reFungibleAbi.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    "name": "MintingFinished",56    "type": "event"57  },58  {59    "anonymous": false,60    "inputs": [61      {62        "indexed": true,63        "internalType": "address",64        "name": "from",65        "type": "address"66      },67      {68        "indexed": true,69        "internalType": "address",70        "name": "to",71        "type": "address"72      },73      {74        "indexed": true,75        "internalType": "uint256",76        "name": "tokenId",77        "type": "uint256"78      }79    ],80    "name": "Transfer",81    "type": "event"82  },83  {84    "inputs": [85      { "internalType": "address", "name": "newAdmin", "type": "address" }86    ],87    "name": "addCollectionAdmin",88    "outputs": [],89    "stateMutability": "nonpayable",90    "type": "function"91  },92  {93    "inputs": [94      {95        "components": [96          { "internalType": "address", "name": "field_0", "type": "address" },97          { "internalType": "uint256", "name": "field_1", "type": "uint256" }98        ],99        "internalType": "struct Tuple6",100        "name": "newAdmin",101        "type": "tuple"102      }103    ],104    "name": "addCollectionAdminCross",105    "outputs": [],106    "stateMutability": "nonpayable",107    "type": "function"108  },109  {110    "inputs": [111      { "internalType": "address", "name": "user", "type": "address" }112    ],113    "name": "addToCollectionAllowList",114    "outputs": [],115    "stateMutability": "nonpayable",116    "type": "function"117  },118  {119    "inputs": [120      {121        "components": [122          { "internalType": "address", "name": "field_0", "type": "address" },123          { "internalType": "uint256", "name": "field_1", "type": "uint256" }124        ],125        "internalType": "struct Tuple6",126        "name": "user",127        "type": "tuple"128      }129    ],130    "name": "addToCollectionAllowListCross",131    "outputs": [],132    "stateMutability": "nonpayable",133    "type": "function"134  },135  {136    "inputs": [137      { "internalType": "address", "name": "user", "type": "address" }138    ],139    "name": "allowed",140    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],141    "stateMutability": "view",142    "type": "function"143  },144  {145    "inputs": [146      { "internalType": "address", "name": "approved", "type": "address" },147      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }148    ],149    "name": "approve",150    "outputs": [],151    "stateMutability": "nonpayable",152    "type": "function"153  },154  {155    "inputs": [156      { "internalType": "address", "name": "owner", "type": "address" }157    ],158    "name": "balanceOf",159    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],160    "stateMutability": "view",161    "type": "function"162  },163  {164    "inputs": [165      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }166    ],167    "name": "burn",168    "outputs": [],169    "stateMutability": "nonpayable",170    "type": "function"171  },172  {173    "inputs": [174      { "internalType": "address", "name": "from", "type": "address" },175      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }176    ],177    "name": "burnFrom",178    "outputs": [],179    "stateMutability": "nonpayable",180    "type": "function"181  },182  {183    "inputs": [184      {185        "components": [186          { "internalType": "address", "name": "field_0", "type": "address" },187          { "internalType": "uint256", "name": "field_1", "type": "uint256" }188        ],189        "internalType": "struct Tuple6",190        "name": "from",191        "type": "tuple"192      },193      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }194    ],195    "name": "burnFromCross",196    "outputs": [],197    "stateMutability": "nonpayable",198    "type": "function"199  },200  {201    "inputs": [202      { "internalType": "address", "name": "newOwner", "type": "address" }203    ],204    "name": "changeCollectionOwner",205    "outputs": [],206    "stateMutability": "nonpayable",207    "type": "function"208  },209  {210    "inputs": [],211    "name": "collectionAdmins",212    "outputs": [213      {214        "components": [215          { "internalType": "address", "name": "field_0", "type": "address" },216          { "internalType": "uint256", "name": "field_1", "type": "uint256" }217        ],218        "internalType": "struct Tuple6[]",219        "name": "",220        "type": "tuple[]"221      }222    ],223    "stateMutability": "view",224    "type": "function"225  },226  {227    "inputs": [],228    "name": "collectionOwner",229    "outputs": [230      {231        "components": [232          { "internalType": "address", "name": "field_0", "type": "address" },233          { "internalType": "uint256", "name": "field_1", "type": "uint256" }234        ],235        "internalType": "struct Tuple6",236        "name": "",237        "type": "tuple"238      }239    ],240    "stateMutability": "view",241    "type": "function"242  },243  {244    "inputs": [245      { "internalType": "string[]", "name": "keys", "type": "string[]" }246    ],247    "name": "collectionProperties",248    "outputs": [249      {250        "components": [251          { "internalType": "string", "name": "field_0", "type": "string" },252          { "internalType": "bytes", "name": "field_1", "type": "bytes" }253        ],254        "internalType": "struct Tuple19[]",255        "name": "",256        "type": "tuple[]"257      }258    ],259    "stateMutability": "view",260    "type": "function"261  },262  {263    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],264    "name": "collectionProperty",265    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],266    "stateMutability": "view",267    "type": "function"268  },269  {270    "inputs": [],271    "name": "collectionSponsor",272    "outputs": [273      {274        "components": [275          { "internalType": "address", "name": "field_0", "type": "address" },276          { "internalType": "uint256", "name": "field_1", "type": "uint256" }277        ],278        "internalType": "struct Tuple6",279        "name": "",280        "type": "tuple"281      }282    ],283    "stateMutability": "view",284    "type": "function"285  },286  {287    "inputs": [],288    "name": "confirmCollectionSponsorship",289    "outputs": [],290    "stateMutability": "nonpayable",291    "type": "function"292  },293  {294    "inputs": [],295    "name": "contractAddress",296    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],297    "stateMutability": "view",298    "type": "function"299  },300  {301    "inputs": [302      { "internalType": "string[]", "name": "keys", "type": "string[]" }303    ],304    "name": "deleteCollectionProperties",305    "outputs": [],306    "stateMutability": "nonpayable",307    "type": "function"308  },309  {310    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],311    "name": "deleteCollectionProperty",312    "outputs": [],313    "stateMutability": "nonpayable",314    "type": "function"315  },316  {317    "inputs": [318      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },319      { "internalType": "string", "name": "key", "type": "string" }320    ],321    "name": "deleteProperty",322    "outputs": [],323    "stateMutability": "nonpayable",324    "type": "function"325  },326  {327    "inputs": [],328    "name": "finishMinting",329    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],330    "stateMutability": "nonpayable",331    "type": "function"332  },333  {334    "inputs": [335      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }336    ],337    "name": "getApproved",338    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],339    "stateMutability": "view",340    "type": "function"341  },342  {343    "inputs": [],344    "name": "hasCollectionPendingSponsor",345    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],346    "stateMutability": "view",347    "type": "function"348  },349  {350    "inputs": [351      { "internalType": "address", "name": "owner", "type": "address" },352      { "internalType": "address", "name": "operator", "type": "address" }353    ],354    "name": "isApprovedForAll",355    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],356    "stateMutability": "view",357    "type": "function"358  },359  {360    "inputs": [361      { "internalType": "address", "name": "user", "type": "address" }362    ],363    "name": "isOwnerOrAdmin",364    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],365    "stateMutability": "view",366    "type": "function"367  },368  {369    "inputs": [370      {371        "components": [372          { "internalType": "address", "name": "field_0", "type": "address" },373          { "internalType": "uint256", "name": "field_1", "type": "uint256" }374        ],375        "internalType": "struct Tuple6",376        "name": "user",377        "type": "tuple"378      }379    ],380    "name": "isOwnerOrAdminCross",381    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],382    "stateMutability": "view",383    "type": "function"384  },385  {386    "inputs": [{ "internalType": "address", "name": "to", "type": "address" }],387    "name": "mint",388    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],389    "stateMutability": "nonpayable",390    "type": "function"391  },392  {393    "inputs": [394      { "internalType": "address", "name": "to", "type": "address" },395      { "internalType": "string", "name": "tokenUri", "type": "string" }396    ],397    "name": "mintWithTokenURI",398    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],399    "stateMutability": "nonpayable",400    "type": "function"401  },402  {403    "inputs": [],404    "name": "mintingFinished",405    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],406    "stateMutability": "view",407    "type": "function"408  },409  {410    "inputs": [],411    "name": "name",412    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],413    "stateMutability": "view",414    "type": "function"415  },416  {417    "inputs": [],418    "name": "nextTokenId",419    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],420    "stateMutability": "view",421    "type": "function"422  },423  {424    "inputs": [425      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }426    ],427    "name": "ownerOf",428    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],429    "stateMutability": "view",430    "type": "function"431  },432  {433    "inputs": [434      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },435      { "internalType": "string", "name": "key", "type": "string" }436    ],437    "name": "property",438    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],439    "stateMutability": "view",440    "type": "function"441  },442  {443    "inputs": [444      { "internalType": "address", "name": "admin", "type": "address" }445    ],446    "name": "removeCollectionAdmin",447    "outputs": [],448    "stateMutability": "nonpayable",449    "type": "function"450  },451  {452    "inputs": [453      {454        "components": [455          { "internalType": "address", "name": "field_0", "type": "address" },456          { "internalType": "uint256", "name": "field_1", "type": "uint256" }457        ],458        "internalType": "struct Tuple6",459        "name": "admin",460        "type": "tuple"461      }462    ],463    "name": "removeCollectionAdminCross",464    "outputs": [],465    "stateMutability": "nonpayable",466    "type": "function"467  },468  {469    "inputs": [],470    "name": "removeCollectionSponsor",471    "outputs": [],472    "stateMutability": "nonpayable",473    "type": "function"474  },475  {476    "inputs": [477      { "internalType": "address", "name": "user", "type": "address" }478    ],479    "name": "removeFromCollectionAllowList",480    "outputs": [],481    "stateMutability": "nonpayable",482    "type": "function"483  },484  {485    "inputs": [486      {487        "components": [488          { "internalType": "address", "name": "field_0", "type": "address" },489          { "internalType": "uint256", "name": "field_1", "type": "uint256" }490        ],491        "internalType": "struct Tuple6",492        "name": "user",493        "type": "tuple"494      }495    ],496    "name": "removeFromCollectionAllowListCross",497    "outputs": [],498    "stateMutability": "nonpayable",499    "type": "function"500  },501  {502    "inputs": [503      { "internalType": "address", "name": "from", "type": "address" },504      { "internalType": "address", "name": "to", "type": "address" },505      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }506    ],507    "name": "safeTransferFrom",508    "outputs": [],509    "stateMutability": "nonpayable",510    "type": "function"511  },512  {513    "inputs": [514      { "internalType": "address", "name": "from", "type": "address" },515      { "internalType": "address", "name": "to", "type": "address" },516      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },517      { "internalType": "bytes", "name": "data", "type": "bytes" }518    ],519    "name": "safeTransferFromWithData",520    "outputs": [],521    "stateMutability": "nonpayable",522    "type": "function"523  },524  {525    "inputs": [526      { "internalType": "address", "name": "operator", "type": "address" },527      { "internalType": "bool", "name": "approved", "type": "bool" }528    ],529    "name": "setApprovalForAll",530    "outputs": [],531    "stateMutability": "nonpayable",532    "type": "function"533  },534  {535    "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],536    "name": "setCollectionAccess",537    "outputs": [],538    "stateMutability": "nonpayable",539    "type": "function"540  },541  {542    "inputs": [543      { "internalType": "string", "name": "limit", "type": "string" },544      { "internalType": "uint32", "name": "value", "type": "uint32" }545    ],546    "name": "setCollectionLimit",547    "outputs": [],548    "stateMutability": "nonpayable",549    "type": "function"550  },551  {552    "inputs": [553      { "internalType": "string", "name": "limit", "type": "string" },554      { "internalType": "bool", "name": "value", "type": "bool" }555    ],556    "name": "setCollectionLimit",557    "outputs": [],558    "stateMutability": "nonpayable",559    "type": "function"560  },561  {562    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],563    "name": "setCollectionMintMode",564    "outputs": [],565    "stateMutability": "nonpayable",566    "type": "function"567  },568  {569    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],570    "name": "setCollectionNesting",571    "outputs": [],572    "stateMutability": "nonpayable",573    "type": "function"574  },575  {576    "inputs": [577      { "internalType": "bool", "name": "enable", "type": "bool" },578      {579        "internalType": "address[]",580        "name": "collections",581        "type": "address[]"582      }583    ],584    "name": "setCollectionNesting",585    "outputs": [],586    "stateMutability": "nonpayable",587    "type": "function"588  },589  {590    "inputs": [591      {592        "components": [593          { "internalType": "string", "name": "field_0", "type": "string" },594          { "internalType": "bytes", "name": "field_1", "type": "bytes" }595        ],596        "internalType": "struct Tuple19[]",597        "name": "properties",598        "type": "tuple[]"599      }600    ],601    "name": "setCollectionProperties",602    "outputs": [],603    "stateMutability": "nonpayable",604    "type": "function"605  },606  {607    "inputs": [608      { "internalType": "string", "name": "key", "type": "string" },609      { "internalType": "bytes", "name": "value", "type": "bytes" }610    ],611    "name": "setCollectionProperty",612    "outputs": [],613    "stateMutability": "nonpayable",614    "type": "function"615  },616  {617    "inputs": [618      { "internalType": "address", "name": "sponsor", "type": "address" }619    ],620    "name": "setCollectionSponsor",621    "outputs": [],622    "stateMutability": "nonpayable",623    "type": "function"624  },625  {626    "inputs": [627      {628        "components": [629          { "internalType": "address", "name": "field_0", "type": "address" },630          { "internalType": "uint256", "name": "field_1", "type": "uint256" }631        ],632        "internalType": "struct Tuple6",633        "name": "sponsor",634        "type": "tuple"635      }636    ],637    "name": "setCollectionSponsorCross",638    "outputs": [],639    "stateMutability": "nonpayable",640    "type": "function"641  },642  {643    "inputs": [644      {645        "components": [646          { "internalType": "address", "name": "field_0", "type": "address" },647          { "internalType": "uint256", "name": "field_1", "type": "uint256" }648        ],649        "internalType": "struct Tuple6",650        "name": "newOwner",651        "type": "tuple"652      }653    ],654    "name": "setOwnerCross",655    "outputs": [],656    "stateMutability": "nonpayable",657    "type": "function"658  },659  {660    "inputs": [661      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },662      { "internalType": "string", "name": "key", "type": "string" },663      { "internalType": "bytes", "name": "value", "type": "bytes" }664    ],665    "name": "setProperty",666    "outputs": [],667    "stateMutability": "nonpayable",668    "type": "function"669  },670  {671    "inputs": [672      { "internalType": "string", "name": "key", "type": "string" },673      { "internalType": "bool", "name": "isMutable", "type": "bool" },674      { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },675      { "internalType": "bool", "name": "tokenOwner", "type": "bool" }676    ],677    "name": "setTokenPropertyPermission",678    "outputs": [],679    "stateMutability": "nonpayable",680    "type": "function"681  },682  {683    "inputs": [684      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }685    ],686    "name": "supportsInterface",687    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],688    "stateMutability": "view",689    "type": "function"690  },691  {692    "inputs": [],693    "name": "symbol",694    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],695    "stateMutability": "view",696    "type": "function"697  },698  {699    "inputs": [700      { "internalType": "uint256", "name": "index", "type": "uint256" }701    ],702    "name": "tokenByIndex",703    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],704    "stateMutability": "view",705    "type": "function"706  },707  {708    "inputs": [709      { "internalType": "uint256", "name": "token", "type": "uint256" }710    ],711    "name": "tokenContractAddress",712    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],713    "stateMutability": "view",714    "type": "function"715  },716  {717    "inputs": [718      { "internalType": "address", "name": "owner", "type": "address" },719      { "internalType": "uint256", "name": "index", "type": "uint256" }720    ],721    "name": "tokenOfOwnerByIndex",722    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],723    "stateMutability": "view",724    "type": "function"725  },726  {727    "inputs": [728      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }729    ],730    "name": "tokenURI",731    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],732    "stateMutability": "view",733    "type": "function"734  },735  {736    "inputs": [],737    "name": "totalSupply",738    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],739    "stateMutability": "view",740    "type": "function"741  },742  {743    "inputs": [744      { "internalType": "address", "name": "to", "type": "address" },745      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }746    ],747    "name": "transfer",748    "outputs": [],749    "stateMutability": "nonpayable",750    "type": "function"751  },752  {753    "inputs": [754      { "internalType": "address", "name": "from", "type": "address" },755      { "internalType": "address", "name": "to", "type": "address" },756      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }757    ],758    "name": "transferFrom",759    "outputs": [],760    "stateMutability": "nonpayable",761    "type": "function"762  },763  {764    "inputs": [765      {766        "components": [767          { "internalType": "address", "name": "field_0", "type": "address" },768          { "internalType": "uint256", "name": "field_1", "type": "uint256" }769        ],770        "internalType": "struct Tuple6",771        "name": "from",772        "type": "tuple"773      },774      {775        "components": [776          { "internalType": "address", "name": "field_0", "type": "address" },777          { "internalType": "uint256", "name": "field_1", "type": "uint256" }778        ],779        "internalType": "struct Tuple6",780        "name": "to",781        "type": "tuple"782      },783      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }784    ],785    "name": "transferFromCross",786    "outputs": [],787    "stateMutability": "nonpayable",788    "type": "function"789  },790  {791    "inputs": [],792    "name": "uniqueCollectionType",793    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],794    "stateMutability": "view",795    "type": "function"796  }797]
modifiedtests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/types.ts
+++ b/tests/src/eth/util/playgrounds/types.ts
@@ -19,3 +19,6 @@
   readonly field_0: string,
   readonly field_1: string | Uint8Array,
 }
+
+export type EthProperty = string[];
+
modifiedtests/src/eth/util/playgrounds/unique.dev.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/unique.dev.ts
+++ b/tests/src/eth/util/playgrounds/unique.dev.ts
@@ -18,7 +18,7 @@
 
 import {DevUniqueHelper} from '../../../util/playgrounds/unique.dev';
 
-import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent} from './types';
+import {ContractImports, CompiledContract, TEthCrossAccount, NormalizedEvent, EthProperty} from './types';
 
 // Native contracts ABI
 import collectionHelpersAbi from '../../collectionHelpersAbi.json';
@@ -28,6 +28,7 @@
 import refungibleTokenAbi from '../../reFungibleTokenAbi.json';
 import contractHelpersAbi from './../contractHelpersAbi.json';
 import {ICrossAccountId, TEthereumAccount} from '../../../util/playgrounds/types';
+import {TCollectionMode} from '../../../util/playgrounds/types';
 
 class EthGroupBase {
   helper: EthUniqueHelper;
@@ -107,7 +108,7 @@
     return new web3.eth.Contract(collectionHelpersAbi as any, '0x6c4e9fe1ae37a41e93cee429e8e1881abdcbb54f', {from: caller, gas: this.helper.eth.DEFAULT_GAS});
   }
 
-  collection(address: string, mode: 'nft' | 'rft' | 'ft', caller?: string): Contract {
+  collection(address: string, mode: TCollectionMode, caller?: string): Contract {
     const abi = {
       'nft': nonFungibleAbi,
       'rft': refungibleAbi,
@@ -336,8 +337,16 @@
   normalizeAddress(address: string): string {
     return '0x' + address.substring(address.length - 40);
   }
-}
+}  
 
+export class EthPropertyGroup extends EthGroupBase {
+  property(key: string, value: string): EthProperty {
+    return [
+      key, 
+      '0x'+Buffer.from(value).toString('hex'),
+    ];
+  }
+}
 export type EthUniqueHelperConstructor = new (...args: any[]) => EthUniqueHelper;
 
 export class EthCrossAccountGroup extends EthGroupBase {
@@ -369,6 +378,7 @@
   ethNativeContract: NativeContractGroup;
   ethContract: ContractGroup;
   ethCrossAccount: EthCrossAccountGroup;
+  ethProperty: EthPropertyGroup;
 
   constructor(logger: { log: (msg: any, level: any) => void, level: any }, options: {[key: string]: any} = {}) {
     options.helperBase = options.helperBase ?? EthUniqueHelper;
@@ -379,6 +389,7 @@
     this.ethCrossAccount = new EthCrossAccountGroup(this);
     this.ethNativeContract = new NativeContractGroup(this);
     this.ethContract = new ContractGroup(this);
+    this.ethProperty = new EthPropertyGroup(this);
   }
 
   getWeb3(): Web3 {
modifiedtests/src/util/playgrounds/types.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/types.ts
+++ b/tests/src/util/playgrounds/types.ts
@@ -224,3 +224,4 @@
 export type TRelayNetworks = 'rococo' | 'westend';
 export type TNetworks = TUniqueNetworks | TSiblingNetworkds | TRelayNetworks;
 export type TSigner = IKeyringPair; // | 'string'
+export type TCollectionMode = 'nft' | 'rft' | 'ft';