git.delta.rocks / unique-network / refs/commits / 984c76e4f44f

difftreelog

CORE-386 Implement eth methods

Trubnikov Sergey2022-06-01parent: #b83ad95.patch.diff
in: master

5 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -253,6 +253,42 @@
 		save(self);
 		Ok(())
 	}
+
+	fn set_access(&mut self, caller: caller, mode: string) -> Result<void> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		self.check_is_owner_or_admin(&caller)
+			.map_err(dispatch_to_evm::<T>)?;
+		self.collection.permissions.access = Some(match mode.as_str() {
+			"Normal" => AccessMode::Normal,
+			"AllowList" => AccessMode::AllowList,
+			_ => return Err("Not supported access mode".into()),
+		});
+		save(self);
+		Ok(())
+	}
+
+	fn add_to_allow_list(&self, caller: caller, user: address) -> Result<void> {
+		let caller = check_is_owner_or_admin(caller, self)?;
+		let user = T::CrossAccountId::from_eth(user);
+		<Pallet<T>>::toggle_allowlist(self, &caller, &user, true)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
+	fn remove_from_allow_list(&self, caller: caller, user: address) -> Result<void> {
+		let caller = check_is_owner_or_admin(caller, self)?;
+		let user = T::CrossAccountId::from_eth(user);
+		<Pallet<T>>::toggle_allowlist(self, &caller, &user, false)
+			.map_err(dispatch_to_evm::<T>)?;
+		Ok(())
+	}
+
+	fn set_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
+		check_is_owner_or_admin(caller, self)?;
+		self.collection.permissions.mint_mode = Some(mode);
+		save(self);
+		Ok(())
+	}
 }
 
 fn check_is_owner<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<void> {
@@ -263,10 +299,7 @@
 	Ok(())
 }
 
-fn check_is_owner_or_admin<T: Config>(
-	caller: caller,
-	collection: &CollectionHandle<T>,
-) -> Result<T::CrossAccountId> {
+fn check_is_owner_or_admin<T: Config>(caller: caller, collection: &CollectionHandle<T>) -> Result<T::CrossAccountId> {
 	let caller = T::CrossAccountId::from_eth(caller);
 	collection
 		.check_is_owner_or_admin(&caller)
@@ -274,11 +307,7 @@
 	Ok(caller)
 }
 
-fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {
-	// TODO possibly delete for the lack of transaction
-	collection
-		.check_is_internal()
-		.map_err(dispatch_to_evm::<T>)?;
+fn save<T: Config>(collection: &CollectionHandle<T>) {
 	<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());
 	Ok(())
 }
modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -51,105 +51,6 @@
 	event MintingFinished();
 }
 
-// Selector: 3a54513b
-contract Collection is Dummy, ERC165 {
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		public
-	{
-		require(false, stub_error);
-		key;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) public {
-		require(false, stub_error);
-		key;
-		dummy = 0;
-	}
-
-	// Throws error if key not found
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
-		public
-		view
-		returns (bytes memory)
-	{
-		require(false, stub_error);
-		key;
-		dummy;
-		return hex"";
-	}
-
-	// Selector: ethSetSponsor(address) 8f9af356
-	function ethSetSponsor(address sponsor) public {
-		require(false, stub_error);
-		sponsor;
-		dummy = 0;
-	}
-
-	// Selector: ethConfirmSponsorship() a8580d1a
-	function ethConfirmSponsorship() public {
-		require(false, stub_error);
-		dummy = 0;
-	}
-
-	// Selector: setLimit(string,uint32) 68db30ca
-	function setLimit(string memory limit, uint32 value) public {
-		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: setLimit(string,bool) ea67e4c2
-	function setLimit(string memory limit, bool value) public {
-		require(false, stub_error);
-		limit;
-		value;
-		dummy = 0;
-	}
-
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() public view returns (address) {
-		require(false, stub_error);
-		dummy;
-		return 0x0000000000000000000000000000000000000000;
-	}
-
-	// Selector: addAdmin(address) 70480275
-	function addAdmin(address newAdmin) public view {
-		require(false, stub_error);
-		newAdmin;
-		dummy;
-	}
-
-	// Selector: removeAdmin(address) 1785f53c
-	function removeAdmin(address admin) public view {
-		require(false, stub_error);
-		admin;
-		dummy;
-	}
-
-	// Selector: setNesting(bool) e8fc50dd
-	function setNesting(bool enable) public {
-		require(false, stub_error);
-		enable;
-		dummy = 0;
-	}
-
-	// Selector: setNesting(bool,address[]) 7df12a9a
-	function setNesting(bool enable, address[] memory collections) public {
-		require(false, stub_error);
-		enable;
-		collections;
-		dummy = 0;
-	}
-}
-
 // Selector: 41369377
 contract TokenProperties is Dummy, ERC165 {
 	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
@@ -479,6 +380,133 @@
 	}
 }
 
+// Selector: f56cd7fa
+contract Collection is Dummy, ERC165 {
+	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	function setCollectionProperty(string memory key, bytes memory value)
+		public
+	{
+		require(false, stub_error);
+		key;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: deleteCollectionProperty(string) 7b7debce
+	function deleteCollectionProperty(string memory key) public {
+		require(false, stub_error);
+		key;
+		dummy = 0;
+	}
+
+	// Throws error if key not found
+	//
+	// Selector: collectionProperty(string) cf24fd6d
+	function collectionProperty(string memory key)
+		public
+		view
+		returns (bytes memory)
+	{
+		require(false, stub_error);
+		key;
+		dummy;
+		return hex"";
+	}
+
+	// Selector: ethSetSponsor(address) 8f9af356
+	function ethSetSponsor(address sponsor) public {
+		require(false, stub_error);
+		sponsor;
+		dummy = 0;
+	}
+
+	// Selector: ethConfirmSponsorship() a8580d1a
+	function ethConfirmSponsorship() public {
+		require(false, stub_error);
+		dummy = 0;
+	}
+
+	// Selector: setLimit(string,uint32) 68db30ca
+	function setLimit(string memory limit, uint32 value) public {
+		require(false, stub_error);
+		limit;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: setLimit(string,bool) ea67e4c2
+	function setLimit(string memory limit, bool value) public {
+		require(false, stub_error);
+		limit;
+		value;
+		dummy = 0;
+	}
+
+	// Selector: contractAddress() f6b4dfb4
+	function contractAddress() public view returns (address) {
+		require(false, stub_error);
+		dummy;
+		return 0x0000000000000000000000000000000000000000;
+	}
+
+	// Selector: addAdmin(address) 70480275
+	function addAdmin(address newAdmin) public view {
+		require(false, stub_error);
+		newAdmin;
+		dummy;
+	}
+
+	// Selector: removeAdmin(address) 1785f53c
+	function removeAdmin(address admin) public view {
+		require(false, stub_error);
+		admin;
+		dummy;
+	}
+
+	// Selector: setNesting(bool) e8fc50dd
+	function setNesting(bool enable) public {
+		require(false, stub_error);
+		enable;
+		dummy = 0;
+	}
+
+	// Selector: setNesting(bool,address[]) 7df12a9a
+	function setNesting(bool enable, address[] memory collections) public {
+		require(false, stub_error);
+		enable;
+		collections;
+		dummy = 0;
+	}
+
+	// Selector: setAccess(string) 488f56aa
+	function setAccess(string memory mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+
+	// Selector: addToAllowList(address) 31f59102
+	function addToAllowList(address user) public view {
+		require(false, stub_error);
+		user;
+		dummy;
+	}
+
+	// Selector: removeFromAllowList(address) eba8dabc
+	function removeFromAllowList(address user) public view {
+		require(false, stub_error);
+		user;
+		dummy;
+	}
+
+	// Selector: setMintMode(bool) 5dea9bd5
+	function setMintMode(bool mode) public {
+		require(false, stub_error);
+		mode;
+		dummy = 0;
+	}
+}
+
 contract UniqueNFT is
 	Dummy,
 	ERC165,
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -42,51 +42,6 @@
 	event MintingFinished();
 }
 
-// Selector: 3a54513b
-interface Collection is Dummy, ERC165 {
-	// Selector: setCollectionProperty(string,bytes) 2f073f66
-	function setCollectionProperty(string memory key, bytes memory value)
-		external;
-
-	// Selector: deleteCollectionProperty(string) 7b7debce
-	function deleteCollectionProperty(string memory key) external;
-
-	// Throws error if key not found
-	//
-	// Selector: collectionProperty(string) cf24fd6d
-	function collectionProperty(string memory key)
-		external
-		view
-		returns (bytes memory);
-
-	// Selector: ethSetSponsor(address) 8f9af356
-	function ethSetSponsor(address sponsor) external;
-
-	// Selector: ethConfirmSponsorship() a8580d1a
-	function ethConfirmSponsorship() external;
-
-	// Selector: setLimit(string,uint32) 68db30ca
-	function setLimit(string memory limit, uint32 value) external;
-
-	// Selector: setLimit(string,bool) ea67e4c2
-	function setLimit(string memory limit, bool value) external;
-
-	// Selector: contractAddress() f6b4dfb4
-	function contractAddress() external view returns (address);
-
-	// Selector: addAdmin(address) 70480275
-	function addAdmin(address newAdmin) external view;
-
-	// Selector: removeAdmin(address) 1785f53c
-	function removeAdmin(address admin) external view;
-
-	// Selector: setNesting(bool) e8fc50dd
-	function setNesting(bool enable) external;
-
-	// Selector: setNesting(bool,address[]) 7df12a9a
-	function setNesting(bool enable, address[] memory collections) external;
-}
-
 // Selector: 41369377
 interface TokenProperties is Dummy, ERC165 {
 	// Selector: setTokenPropertyPermission(string,bool,bool,bool) 222d97fa
@@ -258,6 +213,63 @@
 		returns (bool);
 }
 
+// Selector: f56cd7fa
+interface Collection is Dummy, ERC165 {
+	// Selector: setCollectionProperty(string,bytes) 2f073f66
+	function setCollectionProperty(string memory key, bytes memory value)
+		external;
+
+	// Selector: deleteCollectionProperty(string) 7b7debce
+	function deleteCollectionProperty(string memory key) external;
+
+	// Throws error if key not found
+	//
+	// Selector: collectionProperty(string) cf24fd6d
+	function collectionProperty(string memory key)
+		external
+		view
+		returns (bytes memory);
+
+	// Selector: ethSetSponsor(address) 8f9af356
+	function ethSetSponsor(address sponsor) external;
+
+	// Selector: ethConfirmSponsorship() a8580d1a
+	function ethConfirmSponsorship() external;
+
+	// Selector: setLimit(string,uint32) 68db30ca
+	function setLimit(string memory limit, uint32 value) external;
+
+	// Selector: setLimit(string,bool) ea67e4c2
+	function setLimit(string memory limit, bool value) external;
+
+	// Selector: contractAddress() f6b4dfb4
+	function contractAddress() external view returns (address);
+
+	// Selector: addAdmin(address) 70480275
+	function addAdmin(address newAdmin) external view;
+
+	// Selector: removeAdmin(address) 1785f53c
+	function removeAdmin(address admin) external view;
+
+	// Selector: setNesting(bool) e8fc50dd
+	function setNesting(bool enable) external;
+
+	// Selector: setNesting(bool,address[]) 7df12a9a
+	function setNesting(bool enable, address[] memory collections) external;
+
+	// Selector: setAccess(string) 488f56aa
+	function setAccess(string memory mode) external;
+
+	// Selector: addToAllowList(address) 31f59102
+	function addToAllowList(address user) external view;
+
+	// Selector: removeFromAllowList(address) eba8dabc
+	function removeFromAllowList(address user) external view;
+
+	// Selector: setMintMode(bool) 5dea9bd5
+	function setMintMode(bool mode) external;
+}
+
 interface UniqueNFT is
 	Dummy,
 	ERC165,
modifiedtests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth
--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -221,6 +221,7 @@
     expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');
   });
 
+  //TODO: CORE-302 add eth methods
   itWeb3('Sponsoring collection from evm address via access list', async ({api, web3}) => {
     const owner = await createEthAccountWithBalance(api, web3);
     const collectionHelpers = evmCollectionHelpers(web3, owner);
@@ -228,65 +229,60 @@
     const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
     const sponsor = await createEthAccountWithBalance(api, web3);
     const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
+    result = await collectionEvm.methods.ethSetSponsor(sponsor).send({from: owner});
     let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
     expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
     expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
     await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
 
-    await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
+    await collectionEvm.methods.ethConfirmSponsorship().send({from: sponsor});
     collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
     expect(collectionSub.sponsorship.isConfirmed).to.be.true;
     expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
 
     const user = createEthAccount(web3);
-    let nextTokenId = await collectionEvm.methods.nextTokenId().call();
+    const nextTokenId = await collectionEvm.methods.nextTokenId().call();
     expect(nextTokenId).to.be.equal('1');
 
     const oldPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
     expect(oldPermissions.mintMode).to.be.false;
     expect(oldPermissions.access).to.be.equal('Normal');
 
-    //TODO: change value, when enum generated
-    await collectionEvm.methods.setCollectionAccess(1 /*'AllowList'*/).send({from: owner});
-    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
-    await collectionEvm.methods.setCollectionMintMode(true).send({from: owner});
+    await collectionEvm.methods.setAccess('AllowList').send({from: owner});
+    await collectionEvm.methods.addToAllowList(user).send({from: owner});
+    await collectionEvm.methods.setMintMode(true).send({from: owner});
 
     const newPermissions = (await getDetailedCollectionInfo(api, collectionId))!.permissions.toHuman();
     expect(newPermissions.mintMode).to.be.true;
     expect(newPermissions.access).to.be.equal('AllowList');
 
-    const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
-    const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    // const [alicesBalanceBefore] = await getBalance(api, [alicesPublicKey]);
 
-    nextTokenId = await collectionEvm.methods.nextTokenId().call({from: user});
-    expect(nextTokenId).to.be.equal('1');
-    result = await collectionEvm.methods.mintWithTokenURI(
-      user,
-      nextTokenId,
-      'Test URI',
-    ).send({from: user});
-    const events = normalizeEvents(result.events);
-    events[0].address = events[0].address.toLocaleLowerCase();
+    {
+      const nextTokenId = await collectionEvm.methods.nextTokenId().call();
+      expect(nextTokenId).to.be.equal('1');
+      const result = await collectionEvm.methods.mintWithTokenURI(
+        user,
+        nextTokenId,
+        'Test URI',
+      ).call({from: user});
+      console.log(result);
+      const events = normalizeEvents(result.events);
 
-    expect(events).to.be.deep.equal([
-      {
-        address: collectionIdAddress.toLocaleLowerCase(),
-        event: 'Transfer',
-        args: {
-          from: '0x0000000000000000000000000000000000000000',
-          to: user,
-          tokenId: nextTokenId,
+      expect(events).to.be.deep.equal([
+        {
+          collectionIdAddress,
+          event: 'Transfer',
+          args: {
+            from: '0x0000000000000000000000000000000000000000',
+            to: user,
+            tokenId: nextTokenId,
+          },
         },
-      },
-    ]);
-
-    expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+      ]);
 
-    const ownerBalanceAfter = await ethBalanceViaSub(api, owner);
-    expect(ownerBalanceAfter).to.be.eq(ownerBalanceBefore);
-    const sponsorBalanceAfter = await ethBalanceViaSub(api, sponsor);
-    expect(sponsorBalanceAfter < sponsorBalanceBefore).to.be.true;
+      expect(await collectionEvm.methods.tokenURI(nextTokenId).call()).to.be.equal('Test URI');
+    }
   });
 
   itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3}) => {
@@ -296,22 +292,23 @@
     const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
     const sponsor = await createEthAccountWithBalance(api, web3);
     const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
-    result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
+    result = await collectionEvm.methods.ethSetSponsor(sponsor).send();
     let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
     expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
     expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
-    await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
+    await expect(collectionEvm.methods.ethConfirmSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
     const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
-    await sponsorCollection.methods.confirmCollectionSponsorship().send();
+    await sponsorCollection.methods.ethConfirmSponsorship().send();
     collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
     expect(collectionSub.sponsorship.isConfirmed).to.be.true;
     expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
 
     const user = createEthAccount(web3);
-    await collectionEvm.methods.addCollectionAdmin(user).send();
+    await collectionEvm.methods.addAdmin(user).send();
     
     const ownerBalanceBefore = await ethBalanceViaSub(api, owner);
     const sponsorBalanceBefore = await ethBalanceViaSub(api, sponsor);
+    
   
     const userCollectionEvm = evmCollection(web3, user, collectionIdAddress);
     const nextTokenId = await userCollectionEvm.methods.nextTokenId().call();
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
before · tests/src/eth/nonFungibleAbi.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": "addAdmin",88    "outputs": [],89    "stateMutability": "view",90    "type": "function"91  },92  {93    "inputs": [94      { "internalType": "address", "name": "approved", "type": "address" },95      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }96    ],97    "name": "approve",98    "outputs": [],99    "stateMutability": "nonpayable",100    "type": "function"101  },102  {103    "inputs": [104      { "internalType": "address", "name": "owner", "type": "address" }105    ],106    "name": "balanceOf",107    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],108    "stateMutability": "view",109    "type": "function"110  },111  {112    "inputs": [113      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }114    ],115    "name": "burn",116    "outputs": [],117    "stateMutability": "nonpayable",118    "type": "function"119  },120  {121    "inputs": [122      { "internalType": "address", "name": "from", "type": "address" },123      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }124    ],125    "name": "burnFrom",126    "outputs": [],127    "stateMutability": "nonpayable",128    "type": "function"129  },130  {131    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],132    "name": "collectionProperty",133    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],134    "stateMutability": "view",135    "type": "function"136  },137  {138    "inputs": [],139    "name": "confirmCollectionSponsorship",140    "outputs": [],141    "stateMutability": "nonpayable",142    "type": "function"143  },144  {145    "inputs": [],146    "name": "contractAddress",147    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],148    "stateMutability": "view",149    "type": "function"150  },151  {152    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],153    "name": "deleteCollectionProperty",154    "outputs": [],155    "stateMutability": "nonpayable",156    "type": "function"157  },158  {159    "inputs": [160      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },161      { "internalType": "string", "name": "key", "type": "string" }162    ],163    "name": "deleteProperty",164    "outputs": [],165    "stateMutability": "nonpayable",166    "type": "function"167  },168  {169    "inputs": [],170    "name": "finishMinting",171    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],172    "stateMutability": "nonpayable",173    "type": "function"174  },175  {176    "inputs": [177      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }178    ],179    "name": "getApproved",180    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],181    "stateMutability": "view",182    "type": "function"183  },184  {185    "inputs": [186      { "internalType": "address", "name": "owner", "type": "address" },187      { "internalType": "address", "name": "operator", "type": "address" }188    ],189    "name": "isApprovedForAll",190    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],191    "stateMutability": "view",192    "type": "function"193  },194  {195    "inputs": [196      { "internalType": "address", "name": "to", "type": "address" },197      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }198    ],199    "name": "mint",200    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],201    "stateMutability": "nonpayable",202    "type": "function"203  },204  {205    "inputs": [206      { "internalType": "address", "name": "to", "type": "address" },207      { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }208    ],209    "name": "mintBulk",210    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],211    "stateMutability": "nonpayable",212    "type": "function"213  },214  {215    "inputs": [216      { "internalType": "address", "name": "to", "type": "address" },217      {218        "components": [219          { "internalType": "uint256", "name": "field_0", "type": "uint256" },220          { "internalType": "string", "name": "field_1", "type": "string" }221        ],222        "internalType": "struct Tuple0[]",223        "name": "tokens",224        "type": "tuple[]"225      }226    ],227    "name": "mintBulkWithTokenURI",228    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],229    "stateMutability": "nonpayable",230    "type": "function"231  },232  {233    "inputs": [234      { "internalType": "address", "name": "to", "type": "address" },235      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },236      { "internalType": "string", "name": "tokenUri", "type": "string" }237    ],238    "name": "mintWithTokenURI",239    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],240    "stateMutability": "nonpayable",241    "type": "function"242  },243  {244    "inputs": [],245    "name": "mintingFinished",246    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],247    "stateMutability": "view",248    "type": "function"249  },250  {251    "inputs": [],252    "name": "name",253    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],254    "stateMutability": "view",255    "type": "function"256  },257  {258    "inputs": [],259    "name": "nextTokenId",260    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],261    "stateMutability": "view",262    "type": "function"263  },264  {265    "inputs": [266      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }267    ],268    "name": "ownerOf",269    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],270    "stateMutability": "view",271    "type": "function"272  },273  {274    "inputs": [275      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },276      { "internalType": "string", "name": "key", "type": "string" }277    ],278    "name": "property",279    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],280    "stateMutability": "view",281    "type": "function"282  },283  {284    "inputs": [285      { "internalType": "address", "name": "admin", "type": "address" }286    ],287    "name": "removeAdmin",288    "outputs": [],289    "stateMutability": "view",290    "type": "function"291  },292  {293    "inputs": [294      { "internalType": "address", "name": "from", "type": "address" },295      { "internalType": "address", "name": "to", "type": "address" },296      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }297    ],298    "name": "safeTransferFrom",299    "outputs": [],300    "stateMutability": "nonpayable",301    "type": "function"302  },303  {304    "inputs": [305      { "internalType": "address", "name": "from", "type": "address" },306      { "internalType": "address", "name": "to", "type": "address" },307      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },308      { "internalType": "bytes", "name": "data", "type": "bytes" }309    ],310    "name": "safeTransferFromWithData",311    "outputs": [],312    "stateMutability": "nonpayable",313    "type": "function"314  },315  {316    "inputs": [317      { "internalType": "address", "name": "operator", "type": "address" },318      { "internalType": "bool", "name": "approved", "type": "bool" }319    ],320    "name": "setApprovalForAll",321    "outputs": [],322    "stateMutability": "nonpayable",323    "type": "function"324  },325  {326    "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],327    "name": "setCollectionAccess",328    "outputs": [],329    "stateMutability": "nonpayable",330    "type": "function"331  },332  {333    "inputs": [334      { "internalType": "string", "name": "limit", "type": "string" },335      { "internalType": "uint32", "name": "value", "type": "uint32" }336    ],337    "name": "setCollectionLimit",338    "outputs": [],339    "stateMutability": "nonpayable",340    "type": "function"341  },342  {343    "inputs": [344      { "internalType": "string", "name": "limit", "type": "string" },345      { "internalType": "bool", "name": "value", "type": "bool" }346    ],347    "name": "setCollectionLimit",348    "outputs": [],349    "stateMutability": "nonpayable",350    "type": "function"351  },352  {353    "inputs": [{ "internalType": "bool", "name": "mode", "type": "bool" }],354    "name": "setCollectionMintMode",355    "outputs": [],356    "stateMutability": "nonpayable",357    "type": "function"358  },359  {360    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],361    "name": "setCollectionNesting",362    "outputs": [],363    "stateMutability": "nonpayable",364    "type": "function"365  },366  {367    "inputs": [368      { "internalType": "bool", "name": "enable", "type": "bool" },369      {370        "internalType": "address[]",371        "name": "collections",372        "type": "address[]"373      }374    ],375    "name": "setCollectionNesting",376    "outputs": [],377    "stateMutability": "nonpayable",378    "type": "function"379  },380  {381    "inputs": [382      { "internalType": "string", "name": "key", "type": "string" },383      { "internalType": "bytes", "name": "value", "type": "bytes" }384    ],385    "name": "setCollectionProperty",386    "outputs": [],387    "stateMutability": "nonpayable",388    "type": "function"389  },390  {391    "inputs": [392      { "internalType": "address", "name": "sponsor", "type": "address" }393    ],394    "name": "setCollectionSponsor",395    "outputs": [],396    "stateMutability": "nonpayable",397    "type": "function"398  },399  {400    "inputs": [401      { "internalType": "bool", "name": "enable", "type": "bool" },402      {403        "internalType": "address[]",404        "name": "collections",405        "type": "address[]"406      }407    ],408    "name": "setNesting",409    "outputs": [],410    "stateMutability": "nonpayable",411    "type": "function"412  },413  {414    "inputs": [{ "internalType": "bool", "name": "enable", "type": "bool" }],415    "name": "setNesting",416    "outputs": [],417    "stateMutability": "nonpayable",418    "type": "function"419  },420  {421    "inputs": [422      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },423      { "internalType": "string", "name": "key", "type": "string" },424      { "internalType": "bytes", "name": "value", "type": "bytes" }425    ],426    "name": "setProperty",427    "outputs": [],428    "stateMutability": "nonpayable",429    "type": "function"430  },431  {432    "inputs": [433      { "internalType": "string", "name": "key", "type": "string" },434      { "internalType": "bool", "name": "isMutable", "type": "bool" },435      { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },436      { "internalType": "bool", "name": "tokenOwner", "type": "bool" }437    ],438    "name": "setTokenPropertyPermission",439    "outputs": [],440    "stateMutability": "nonpayable",441    "type": "function"442  },443  {444    "inputs": [445      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }446    ],447    "name": "supportsInterface",448    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],449    "stateMutability": "view",450    "type": "function"451  },452  {453    "inputs": [],454    "name": "symbol",455    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],456    "stateMutability": "view",457    "type": "function"458  },459  {460    "inputs": [461      { "internalType": "uint256", "name": "index", "type": "uint256" }462    ],463    "name": "tokenByIndex",464    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],465    "stateMutability": "view",466    "type": "function"467  },468  {469    "inputs": [470      { "internalType": "address", "name": "owner", "type": "address" },471      { "internalType": "uint256", "name": "index", "type": "uint256" }472    ],473    "name": "tokenOfOwnerByIndex",474    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],475    "stateMutability": "view",476    "type": "function"477  },478  {479    "inputs": [480      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }481    ],482    "name": "tokenURI",483    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],484    "stateMutability": "view",485    "type": "function"486  },487  {488    "inputs": [],489    "name": "totalSupply",490    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],491    "stateMutability": "view",492    "type": "function"493  },494  {495    "inputs": [496      { "internalType": "address", "name": "to", "type": "address" },497      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }498    ],499    "name": "transfer",500    "outputs": [],501    "stateMutability": "nonpayable",502    "type": "function"503  },504  {505    "inputs": [506      { "internalType": "address", "name": "from", "type": "address" },507      { "internalType": "address", "name": "to", "type": "address" },508      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }509    ],510    "name": "transferFrom",511    "outputs": [],512    "stateMutability": "nonpayable",513    "type": "function"514  }515]