git.delta.rocks / unique-network / refs/commits / 0dc93c7a2ef6

difftreelog

CORE-302 Fully support create collection from evm

Trubnikov Sergey2022-04-15parent: #ce8ad29.patch.diff
in: master

10 files changed

modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -327,7 +327,7 @@
 	}
 }
 
-#[impl_for_tuples(1, 5)]
+#[impl_for_tuples(1, 12)]
 impl SolidityArguments for Tuple {
 	for_tuples!( where #( Tuple: SolidityArguments ),* );
 
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
before · pallets/evm-contract-helpers/src/eth.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog};19use ethereum as _;20use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};21use pallet_evm::{22	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,23	account::CrossAccountId, Pallet as PalletEvm24};25use sp_core::H160;26use crate::{27	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,28};29use frame_support::traits::Get;30use up_sponsorship::SponsorshipHandler;31use sp_std::vec::Vec;3233struct ContractHelpers<T: Config>(SubstrateRecorder<T>);34impl<T: Config> WithRecorder<T> for ContractHelpers<T> {35	fn recorder(&self) -> &SubstrateRecorder<T> {36		&self.037	}3839	fn into_recorder(self) -> SubstrateRecorder<T> {40		self.041	}42}4344#[derive(ToLog)]45pub enum ContractHelperEvent {46	CollectionCreated {47		#[indexed]48		owner: address,49		#[indexed]50		collection_id: address,51	},52}5354#[solidity_interface(name = "ContractHelpers")]55impl<T: Config> ContractHelpers<T> {56	fn contract_owner(&self, contract_address: address) -> Result<address> {57		Ok(<Owner<T>>::get(contract_address))58	}5960	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {61		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)62	}6364	/// Deprecated65	fn toggle_sponsoring(66		&mut self,67		caller: caller,68		contract_address: address,69		enabled: bool,70	) -> Result<void> {71		<Pallet<T>>::ensure_owner(contract_address, caller)?;72		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);73		Ok(())74	}7576	fn set_sponsoring_mode(77		&mut self,78		caller: caller,79		contract_address: address,80		mode: uint8,81	) -> Result<void> {82		<Pallet<T>>::ensure_owner(contract_address, caller)?;83		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;84		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);85		Ok(())86	}8788	fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {89		Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())90	}9192	fn set_sponsoring_rate_limit(93		&mut self,94		caller: caller,95		contract_address: address,96		rate_limit: uint32,97	) -> Result<void> {98		<Pallet<T>>::ensure_owner(contract_address, caller)?;99		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());100		Ok(())101	}102103	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {104		Ok(<SponsoringRateLimit<T>>::get(contract_address)105			.try_into()106			.map_err(|_| "rate limit > u32::MAX")?)107	}108109	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {110		self.0.consume_sload()?;111		Ok(<Pallet<T>>::allowed(contract_address, user))112	}113114	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {115		Ok(<AllowlistEnabled<T>>::get(contract_address))116	}117118	fn toggle_allowlist(119		&mut self,120		caller: caller,121		contract_address: address,122		enabled: bool,123	) -> Result<void> {124		<Pallet<T>>::ensure_owner(contract_address, caller)?;125		<Pallet<T>>::toggle_allowlist(contract_address, enabled);126		Ok(())127	}128129	fn toggle_allowed(130		&mut self,131		caller: caller,132		contract_address: address,133		user: address,134		allowed: bool,135	) -> Result<void> {136		<Pallet<T>>::ensure_owner(contract_address, caller)?;137		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);138		Ok(())139	}140141	fn create_721_collection(&self, caller: caller) -> Result<address> {142		let caller = T::CrossAccountId::from_eth(caller);143		let collection_id = <pallet_nonfungible::Pallet<T>>::init_collection(144			caller.as_sub().clone(),145			Default::default(),146		)147		.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;148		let address = pallet_common::eth::collection_id_to_address(collection_id);149150		<PalletEvm<T>>::deposit_log(151			ContractHelperEvent::CollectionCreated {152				owner: *caller.as_eth(),153				collection_id: address,154			}155			.to_log(address),156		);157		Ok(address)158	}159}160161pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);162impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {163	fn is_reserved(contract: &sp_core::H160) -> bool {164		contract == &T::ContractAddress::get()165	}166167	fn is_used(contract: &sp_core::H160) -> bool {168		contract == &T::ContractAddress::get()169	}170171	fn call(172		source: &sp_core::H160,173		target: &sp_core::H160,174		gas_left: u64,175		input: &[u8],176		value: sp_core::U256,177	) -> Option<PrecompileResult> {178		// TODO: Extract to another OnMethodCall handler179		if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {180			return Some(Err(PrecompileFailure::Revert {181				exit_status: ExitRevert::Reverted,182				cost: 0,183				output: {184					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));185					writer.string("Target contract is allowlisted");186					writer.finish()187				},188			}));189		}190191		if target != &T::ContractAddress::get() {192			return None;193		}194195		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(gas_left));196		pallet_evm_coder_substrate::call(*source, helpers, value, input)197	}198199	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {200		(contract == &T::ContractAddress::get())201			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())202	}203}204205pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);206impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {207	fn on_create(owner: H160, contract: H160) {208		<Owner<T>>::insert(contract, owner);209	}210}211212pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);213impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>214	for HelpersContractSponsoring<T>215{216	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {217		let mode = <Pallet<T>>::sponsoring_mode(call.0);218		if mode == SponsoringModeT::Disabled {219			return None;220		}221222		if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(call.0, *who.as_eth()) {223			return None;224		}225		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;226227		if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who.as_eth()) {228			let limit = <SponsoringRateLimit<T>>::get(&call.0);229230			let timeout = last_tx_block + limit;231			if block_number < timeout {232				return None;233			}234		}235236		<SponsorBasket<T>>::insert(&call.0, who.as_eth(), block_number);237238		let sponsor = T::CrossAccountId::from_eth(call.0);239		Some(sponsor)240	}241}242243generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);244generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);
after · pallets/evm-contract-helpers/src/eth.rs
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;18use evm_coder::{abi::AbiWriter, execution::*, generate_stubgen, solidity_interface, types::*, ToLog};19use ethereum as _;20use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};21use pallet_evm::{22	ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,23	account::CrossAccountId, Pallet as PalletEvm24};25use sp_core::H160;26use up_data_structs::{27	CreateCollectionData, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,28	MAX_COLLECTION_NAME_LENGTH,29};30use crate::{31	AllowlistEnabled, Config, Owner, Pallet, SponsorBasket, SponsoringRateLimit, SponsoringModeT,32};33use frame_support::traits::Get;34use up_sponsorship::SponsorshipHandler;35use sp_std::vec::Vec;36use alloc::format;3738struct ContractHelpers<T: Config>(SubstrateRecorder<T>);39impl<T: Config> WithRecorder<T> for ContractHelpers<T> {40	fn recorder(&self) -> &SubstrateRecorder<T> {41		&self.042	}4344	fn into_recorder(self) -> SubstrateRecorder<T> {45		self.046	}47}4849#[derive(ToLog)]50pub enum ContractHelperEvent {51	CollectionCreated {52		#[indexed]53		owner: address,54		#[indexed]55		collection_id: address,56	},57}5859#[solidity_interface(name = "ContractHelpers")]60impl<T: Config> ContractHelpers<T> {61	fn contract_owner(&self, contract_address: address) -> Result<address> {62		Ok(<Owner<T>>::get(contract_address))63	}6465	fn sponsoring_enabled(&self, contract_address: address) -> Result<bool> {66		Ok(<Pallet<T>>::sponsoring_mode(contract_address) != SponsoringModeT::Disabled)67	}6869	/// Deprecated70	fn toggle_sponsoring(71		&mut self,72		caller: caller,73		contract_address: address,74		enabled: bool,75	) -> Result<void> {76		<Pallet<T>>::ensure_owner(contract_address, caller)?;77		<Pallet<T>>::toggle_sponsoring(contract_address, enabled);78		Ok(())79	}8081	fn set_sponsoring_mode(82		&mut self,83		caller: caller,84		contract_address: address,85		mode: uint8,86	) -> Result<void> {87		<Pallet<T>>::ensure_owner(contract_address, caller)?;88		let mode = SponsoringModeT::from_eth(mode).ok_or("unknown mode")?;89		<Pallet<T>>::set_sponsoring_mode(contract_address, mode);90		Ok(())91	}9293	fn sponsoring_mode(&self, contract_address: address) -> Result<uint8> {94		Ok(<Pallet<T>>::sponsoring_mode(contract_address).to_eth())95	}9697	fn set_sponsoring_rate_limit(98		&mut self,99		caller: caller,100		contract_address: address,101		rate_limit: uint32,102	) -> Result<void> {103		<Pallet<T>>::ensure_owner(contract_address, caller)?;104		<Pallet<T>>::set_sponsoring_rate_limit(contract_address, rate_limit.into());105		Ok(())106	}107108	fn get_sponsoring_rate_limit(&self, contract_address: address) -> Result<uint32> {109		Ok(<SponsoringRateLimit<T>>::get(contract_address)110			.try_into()111			.map_err(|_| "rate limit > u32::MAX")?)112	}113114	fn allowed(&self, contract_address: address, user: address) -> Result<bool> {115		self.0.consume_sload()?;116		Ok(<Pallet<T>>::allowed(contract_address, user))117	}118119	fn allowlist_enabled(&self, contract_address: address) -> Result<bool> {120		Ok(<AllowlistEnabled<T>>::get(contract_address))121	}122123	fn toggle_allowlist(124		&mut self,125		caller: caller,126		contract_address: address,127		enabled: bool,128	) -> Result<void> {129		<Pallet<T>>::ensure_owner(contract_address, caller)?;130		<Pallet<T>>::toggle_allowlist(contract_address, enabled);131		Ok(())132	}133134	fn toggle_allowed(135		&mut self,136		caller: caller,137		contract_address: address,138		user: address,139		allowed: bool,140	) -> Result<void> {141		<Pallet<T>>::ensure_owner(contract_address, caller)?;142		<Pallet<T>>::toggle_allowed(contract_address, user, allowed);143		Ok(())144	}145146	fn create_721_collection(147		&self,148		caller: caller,149		name: string,150		description: string,151		token_prefix: string,152	) -> Result<address> {153		let caller = T::CrossAccountId::from_eth(caller);154		let name = name155			.encode_utf16()156			.collect::<Vec<u16>>()157			.try_into()158			.map_err(|_| error_feild_too_long("name", MAX_COLLECTION_NAME_LENGTH))?;159		let description = description160			.encode_utf16()161			.collect::<Vec<u16>>()162			.try_into()163			.map_err(|_| error_feild_too_long("description", MAX_COLLECTION_DESCRIPTION_LENGTH))?;164		let token_prefix = token_prefix165			.into_bytes()166			.try_into()167			.map_err(|_| error_feild_too_long("token_prefix", MAX_TOKEN_PREFIX_LENGTH))?;168169		let data = CreateCollectionData {170			name,171			description,172			token_prefix,173			..Default::default()174		};175176		let collection_id =177			<pallet_nonfungible::Pallet<T>>::init_collection(caller.as_sub().clone(), data)178				.map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;179180		let address = pallet_common::eth::collection_id_to_address(collection_id);181182		<PalletEvm<T>>::deposit_log(183			ContractHelperEvent::CollectionCreated {184				owner: *caller.as_eth(),185				collection_id: address,186			}187			.to_log(address),188		);189		Ok(address)190	}191}192193fn error_feild_too_long(feild: &str, bound: u32) -> Error {194	Error::Revert(format!("{} is too long. Max length is {}.", feild, bound))195}196197pub struct HelpersOnMethodCall<T: Config>(PhantomData<*const T>);198impl<T: Config> OnMethodCall<T> for HelpersOnMethodCall<T> {199	fn is_reserved(contract: &sp_core::H160) -> bool {200		contract == &T::ContractAddress::get()201	}202203	fn is_used(contract: &sp_core::H160) -> bool {204		contract == &T::ContractAddress::get()205	}206207	fn call(208		source: &sp_core::H160,209		target: &sp_core::H160,210		gas_left: u64,211		input: &[u8],212		value: sp_core::U256,213	) -> Option<PrecompileResult> {214		// TODO: Extract to another OnMethodCall handler215		if <AllowlistEnabled<T>>::get(target) && !<Pallet<T>>::allowed(*target, *source) {216			return Some(Err(PrecompileFailure::Revert {217				exit_status: ExitRevert::Reverted,218				cost: 0,219				output: {220					let mut writer = AbiWriter::new_call(evm_coder::fn_selector!(Error(string)));221					writer.string("Target contract is allowlisted");222					writer.finish()223				},224			}));225		}226227		if target != &T::ContractAddress::get() {228			return None;229		}230231		let helpers = ContractHelpers::<T>(SubstrateRecorder::<T>::new(gas_left));232		pallet_evm_coder_substrate::call(*source, helpers, value, input)233	}234235	fn get_code(contract: &sp_core::H160) -> Option<Vec<u8>> {236		(contract == &T::ContractAddress::get())237			.then(|| include_bytes!("./stubs/ContractHelpers.raw").to_vec())238	}239}240241pub struct HelpersOnCreate<T: Config>(PhantomData<*const T>);242impl<T: Config> OnCreate<T> for HelpersOnCreate<T> {243	fn on_create(owner: H160, contract: H160) {244		<Owner<T>>::insert(contract, owner);245	}246}247248pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);249impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>250	for HelpersContractSponsoring<T>251{252	fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {253		let mode = <Pallet<T>>::sponsoring_mode(call.0);254		if mode == SponsoringModeT::Disabled {255			return None;256		}257258		if mode == SponsoringModeT::Allowlisted && !<Pallet<T>>::allowed(call.0, *who.as_eth()) {259			return None;260		}261		let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;262263		if let Some(last_tx_block) = <SponsorBasket<T>>::get(&call.0, who.as_eth()) {264			let limit = <SponsoringRateLimit<T>>::get(&call.0);265266			let timeout = last_tx_block + limit;267			if block_number < timeout {268				return None;269			}270		}271272		<SponsorBasket<T>>::insert(&call.0, who.as_eth(), block_number);273274		let sponsor = T::CrossAccountId::from_eth(call.0);275		Some(sponsor)276	}277}278279generate_stubgen!(contract_helpers_impl, ContractHelpersCall<()>, true);280generate_stubgen!(contract_helpers_iface, ContractHelpersCall<()>, false);
modifiedpallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -16,6 +16,9 @@
 
 #![cfg_attr(not(feature = "std"), no_std)]
 
+#[macro_use(format)]
+extern crate alloc;
+
 use codec::{Decode, Encode, MaxEncodedLen};
 pub use pallet::*;
 pub use eth::*;
modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/evm-contract-helpers/src/stubs/ContractHelpers.soldiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
+++ b/pallets/evm-contract-helpers/src/stubs/ContractHelpers.sol
@@ -21,7 +21,7 @@
 	}
 }
 
-// Selector: e123b7a8
+// Selector: ee5467a8
 contract ContractHelpers is Dummy, ERC165 {
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
@@ -145,9 +145,16 @@
 		dummy = 0;
 	}
 
-	// Selector: create721Collection() 9a6bd151
-	function create721Collection() public view returns (address) {
+	// Selector: create721Collection(string,string,string) 951c0151
+	function create721Collection(
+		string memory name,
+		string memory description,
+		string memory tokenPrefix
+	) public view returns (address) {
 		require(false, stub_error);
+		name;
+		description;
+		tokenPrefix;
 		dummy;
 		return 0x0000000000000000000000000000000000000000;
 	}
modifiedtests/src/eth/api/ContractHelpers.soldiffbeforeafterboth
--- a/tests/src/eth/api/ContractHelpers.sol
+++ b/tests/src/eth/api/ContractHelpers.sol
@@ -12,7 +12,7 @@
 	function supportsInterface(bytes4 interfaceID) external view returns (bool);
 }
 
-// Selector: e123b7a8
+// Selector: ee5467a8
 interface ContractHelpers is Dummy, ERC165 {
 	// Selector: contractOwner(address) 5152b14c
 	function contractOwner(address contractAddress)
@@ -72,6 +72,10 @@
 		bool allowed
 	) external;
 
-	// Selector: create721Collection() 9a6bd151
-	function create721Collection() external view returns (address);
+	// Selector: create721Collection(string,string,string) 951c0151
+	function create721Collection(
+		string memory name,
+		string memory description,
+		string memory tokenPrefix
+	) external view returns (address);
 }
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -28,7 +28,7 @@
   usingWeb3,
 } from './util/helpers';
 import {expect} from 'chai';
-import {createCollectionExpectSuccess, createItemExpectSuccess, getCreatedCollectionCount, UNIQUE} from '../util/helpers';
+import {createCollectionExpectSuccess, createItemExpectSuccess, getCreatedCollectionCount, getDetailedCollectionInfo, UNIQUE} from '../util/helpers';
 import nonFungibleAbi from './nonFungibleAbi.json';
 import privateKey from '../substrate/privateKey';
 import {Contract} from 'web3-eth-contract';
@@ -128,14 +128,24 @@
 describe('Create collection from EVM', () => {
   itWeb3('Create collection', async ({api, web3}) => {
     const owner = await createEthAccountWithBalance(api, web3);
-    console.log(owner);
     const helpers = contractHelpers(web3, owner);
+    const collectionName = 'CollectionEVM';
+    const description = 'Some description';
+    const tokenPrefix = 'token prefix';
+
     const collectionCountBefore = await getCreatedCollectionCount(api);
-    const result = await helpers.methods.create721Collection().send();
-    console.log(result.events[0].raw);
-    const collectionId = collectionIdFromAddress(result.events[0].raw.topics[2]);
+    const result = await helpers.methods
+      .create721Collection(collectionName, description, tokenPrefix)
+      .send();
     const collectionCountAfter = await getCreatedCollectionCount(api);
+
+    const collectionId = collectionIdFromAddress(result.events[0].raw.topics[2]);
     expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
     expect(collectionId).to.be.eq(collectionCountAfter);
+    
+    const collection = (await getDetailedCollectionInfo(api, collectionId))!;
+    expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
+    expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
+    expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
   });
 });
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -1,175 +1,173 @@
 [
-    {
-        "constant": false,
-        "inputs": [
-            {
-                "name": "_spender",
-                "type": "address"
-            },
-            {
-                "name": "_value",
-                "type": "uint256"
-            }
-        ],
-        "name": "approve",
-        "outputs": [
-            {
-                "name": "",
-                "type": "bool"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "nonpayable",
-        "type": "function"
-    },
-    {
-        "constant": true,
-        "inputs": [],
-        "name": "totalSupply",
-        "outputs": [
-            {
-                "name": "",
-                "type": "uint256"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "view",
-        "type": "function"
-    },
-    {
-        "constant": false,
-        "inputs": [
-            {
-                "name": "_from",
-                "type": "address"
-            },
-            {
-                "name": "_to",
-                "type": "address"
-            },
-            {
-                "name": "_value",
-                "type": "uint256"
-            }
-        ],
-        "name": "transferFrom",
-        "outputs": [
-            {
-                "name": "",
-                "type": "bool"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "nonpayable",
-        "type": "function"
-    },
-    {
-        "constant": true,
-        "inputs": [
-            {
-                "name": "_owner",
-                "type": "address"
-            }
-        ],
-        "name": "balanceOf",
-        "outputs": [
-            {
-                "name": "balance",
-                "type": "uint256"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "view",
-        "type": "function"
-    },
-    {
-        "constant": false,
-        "inputs": [
-            {
-                "name": "_to",
-                "type": "address"
-            },
-            {
-                "name": "_value",
-                "type": "uint256"
-            }
-        ],
-        "name": "transfer",
-        "outputs": [
-            {
-                "name": "",
-                "type": "bool"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "nonpayable",
-        "type": "function"
-    },
-    {
-        "constant": true,
-        "inputs": [
-            {
-                "name": "_owner",
-                "type": "address"
-            },
-            {
-                "name": "_spender",
-                "type": "address"
-            }
-        ],
-        "name": "allowance",
-        "outputs": [
-            {
-                "name": "",
-                "type": "uint256"
-            }
-        ],
-        "payable": false,
-        "stateMutability": "view",
-        "type": "function"
-    },
-    {
-        "anonymous": false,
-        "inputs": [
-            {
-                "indexed": true,
-                "name": "owner",
-                "type": "address"
-            },
-            {
-                "indexed": true,
-                "name": "spender",
-                "type": "address"
-            },
-            {
-                "indexed": false,
-                "name": "value",
-                "type": "uint256"
-            }
-        ],
-        "name": "Approval",
-        "type": "event"
-    },
-    {
-        "anonymous": false,
-        "inputs": [
-            {
-                "indexed": true,
-                "name": "from",
-                "type": "address"
-            },
-            {
-                "indexed": true,
-                "name": "to",
-                "type": "address"
-            },
-            {
-                "indexed": false,
-                "name": "value",
-                "type": "uint256"
-            }
-        ],
-        "name": "Transfer",
-        "type": "event"
-    }
-]
\ No newline at end of file
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "spender",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "uint256",
+        "name": "value",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "spender", "type": "address" }
+    ],
+    "name": "allowance",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "spender", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "collectionProperty",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "decimals",
+    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "deleteCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "amount", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -1,738 +1,398 @@
 [
-	{
-		"anonymous": false,
-		"inputs": [
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "owner",
-				"type": "address"
-			},
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "approved",
-				"type": "address"
-			},
-			{
-				"indexed": true,
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "Approval",
-		"type": "event"
-	},
-	{
-		"anonymous": false,
-		"inputs": [
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "owner",
-				"type": "address"
-			},
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "operator",
-				"type": "address"
-			},
-			{
-				"indexed": false,
-				"internalType": "bool",
-				"name": "approved",
-				"type": "bool"
-			}
-		],
-		"name": "ApprovalForAll",
-		"type": "event"
-	},
-	{
-		"anonymous": false,
-		"inputs": [],
-		"name": "MintingFinished",
-		"type": "event"
-	},
-	{
-		"anonymous": false,
-		"inputs": [
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "from",
-				"type": "address"
-			},
-			{
-				"indexed": true,
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"indexed": true,
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "Transfer",
-		"type": "event"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "approved",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "approve",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "owner",
-				"type": "address"
-			}
-		],
-		"name": "balanceOf",
-		"outputs": [
-			{
-				"internalType": "uint256",
-				"name": "",
-				"type": "uint256"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "burn",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "from",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "burnFrom",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			}
-		],
-		"name": "collectionProperty",
-		"outputs": [
-			{
-				"internalType": "bytes",
-				"name": "",
-				"type": "bytes"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			}
-		],
-		"name": "deleteCollectionProperty",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			},
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			}
-		],
-		"name": "deleteProperty",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "finishMinting",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "getApproved",
-		"outputs": [
-			{
-				"internalType": "address",
-				"name": "",
-				"type": "address"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "owner",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "operator",
-				"type": "address"
-			}
-		],
-		"name": "isApprovedForAll",
-		"outputs": [
-			{
-				"internalType": "address",
-				"name": "",
-				"type": "address"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "mint",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256[]",
-				"name": "tokenIds",
-				"type": "uint256[]"
-			}
-		],
-		"name": "mintBulk",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"components": [
-					{
-						"internalType": "uint256",
-						"name": "field_0",
-						"type": "uint256"
-					},
-					{
-						"internalType": "string",
-						"name": "field_1",
-						"type": "string"
-					}
-				],
-				"internalType": "struct Tuple0[]",
-				"name": "tokens",
-				"type": "tuple[]"
-			}
-		],
-		"name": "mintBulkWithTokenURI",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			},
-			{
-				"internalType": "string",
-				"name": "tokenUri",
-				"type": "string"
-			}
-		],
-		"name": "mintWithTokenURI",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "mintingFinished",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "name",
-		"outputs": [
-			{
-				"internalType": "string",
-				"name": "",
-				"type": "string"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "nextTokenId",
-		"outputs": [
-			{
-				"internalType": "uint256",
-				"name": "",
-				"type": "uint256"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "ownerOf",
-		"outputs": [
-			{
-				"internalType": "address",
-				"name": "",
-				"type": "address"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			},
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			}
-		],
-		"name": "property",
-		"outputs": [
-			{
-				"internalType": "bytes",
-				"name": "",
-				"type": "bytes"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "from",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "safeTransferFrom",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "from",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			},
-			{
-				"internalType": "bytes",
-				"name": "data",
-				"type": "bytes"
-			}
-		],
-		"name": "safeTransferFromWithData",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "operator",
-				"type": "address"
-			},
-			{
-				"internalType": "bool",
-				"name": "approved",
-				"type": "bool"
-			}
-		],
-		"name": "setApprovalForAll",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			},
-			{
-				"internalType": "bytes",
-				"name": "value",
-				"type": "bytes"
-			}
-		],
-		"name": "setCollectionProperty",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			},
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			},
-			{
-				"internalType": "bytes",
-				"name": "value",
-				"type": "bytes"
-			}
-		],
-		"name": "setProperty",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "string",
-				"name": "key",
-				"type": "string"
-			},
-			{
-				"internalType": "bool",
-				"name": "isMutable",
-				"type": "bool"
-			},
-			{
-				"internalType": "bool",
-				"name": "collectionAdmin",
-				"type": "bool"
-			},
-			{
-				"internalType": "bool",
-				"name": "tokenOwner",
-				"type": "bool"
-			}
-		],
-		"name": "setTokenPropertyPermission",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "bytes4",
-				"name": "interfaceID",
-				"type": "bytes4"
-			}
-		],
-		"name": "supportsInterface",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "symbol",
-		"outputs": [
-			{
-				"internalType": "string",
-				"name": "",
-				"type": "string"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "index",
-				"type": "uint256"
-			}
-		],
-		"name": "tokenByIndex",
-		"outputs": [
-			{
-				"internalType": "uint256",
-				"name": "",
-				"type": "uint256"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "owner",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "index",
-				"type": "uint256"
-			}
-		],
-		"name": "tokenOfOwnerByIndex",
-		"outputs": [
-			{
-				"internalType": "uint256",
-				"name": "",
-				"type": "uint256"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "tokenURI",
-		"outputs": [
-			{
-				"internalType": "string",
-				"name": "",
-				"type": "string"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "totalSupply",
-		"outputs": [
-			{
-				"internalType": "uint256",
-				"name": "",
-				"type": "uint256"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "transfer",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "from",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "to",
-				"type": "address"
-			},
-			{
-				"internalType": "uint256",
-				"name": "tokenId",
-				"type": "uint256"
-			}
-		],
-		"name": "transferFrom",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	}
-]
\ No newline at end of file
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "approved",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Approval",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "owner",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "operator",
+        "type": "address"
+      },
+      {
+        "indexed": false,
+        "internalType": "bool",
+        "name": "approved",
+        "type": "bool"
+      }
+    ],
+    "name": "ApprovalForAll",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [],
+    "name": "MintingFinished",
+    "type": "event"
+  },
+  {
+    "anonymous": false,
+    "inputs": [
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "from",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "address",
+        "name": "to",
+        "type": "address"
+      },
+      {
+        "indexed": true,
+        "internalType": "uint256",
+        "name": "tokenId",
+        "type": "uint256"
+      }
+    ],
+    "name": "Transfer",
+    "type": "event"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "approved", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "approve",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" }
+    ],
+    "name": "balanceOf",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burn",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "burnFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "collectionProperty",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
+    "name": "deleteCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "deleteProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "finishMinting",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "getApproved",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "address", "name": "operator", "type": "address" }
+    ],
+    "name": "isApprovedForAll",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "mint",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256[]", "name": "tokenIds", "type": "uint256[]" }
+    ],
+    "name": "mintBulk",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      {
+        "components": [
+          { "internalType": "uint256", "name": "field_0", "type": "uint256" },
+          { "internalType": "string", "name": "field_1", "type": "string" }
+        ],
+        "internalType": "struct Tuple0[]",
+        "name": "tokens",
+        "type": "tuple[]"
+      }
+    ],
+    "name": "mintBulkWithTokenURI",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "tokenUri", "type": "string" }
+    ],
+    "name": "mintWithTokenURI",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "mintingFinished",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "name",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "nextTokenId",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "ownerOf",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" }
+    ],
+    "name": "property",
+    "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "safeTransferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "bytes", "name": "data", "type": "bytes" }
+    ],
+    "name": "safeTransferFromWithData",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "operator", "type": "address" },
+      { "internalType": "bool", "name": "approved", "type": "bool" }
+    ],
+    "name": "setApprovalForAll",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setCollectionProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bytes", "name": "value", "type": "bytes" }
+    ],
+    "name": "setProperty",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "key", "type": "string" },
+      { "internalType": "bool", "name": "isMutable", "type": "bool" },
+      { "internalType": "bool", "name": "collectionAdmin", "type": "bool" },
+      { "internalType": "bool", "name": "tokenOwner", "type": "bool" }
+    ],
+    "name": "setTokenPropertyPermission",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "symbol",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "owner", "type": "address" },
+      { "internalType": "uint256", "name": "index", "type": "uint256" }
+    ],
+    "name": "tokenOfOwnerByIndex",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "tokenURI",
+    "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [],
+    "name": "totalSupply",
+    "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transfer",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "from", "type": "address" },
+      { "internalType": "address", "name": "to", "type": "address" },
+      { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
+    ],
+    "name": "transferFrom",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]
modifiedtests/src/eth/util/contractHelpersAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/util/contractHelpersAbi.json
+++ b/tests/src/eth/util/contractHelpersAbi.json
@@ -1,248 +1,172 @@
 [
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "user",
-				"type": "address"
-			}
-		],
-		"name": "allowed",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			}
-		],
-		"name": "allowlistEnabled",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			}
-		],
-		"name": "contractOwner",
-		"outputs": [
-			{
-				"internalType": "address",
-				"name": "",
-				"type": "address"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [],
-		"name": "create721Collection",
-		"outputs": [
-			{
-				"internalType": "address",
-				"name": "",
-				"type": "address"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			}
-		],
-		"name": "getSponsoringRateLimit",
-		"outputs": [
-			{
-				"internalType": "uint32",
-				"name": "",
-				"type": "uint32"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "uint8",
-				"name": "mode",
-				"type": "uint8"
-			}
-		],
-		"name": "setSponsoringMode",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "uint32",
-				"name": "rateLimit",
-				"type": "uint32"
-			}
-		],
-		"name": "setSponsoringRateLimit",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			}
-		],
-		"name": "sponsoringEnabled",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			}
-		],
-		"name": "sponsoringMode",
-		"outputs": [
-			{
-				"internalType": "uint8",
-				"name": "",
-				"type": "uint8"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "bytes4",
-				"name": "interfaceID",
-				"type": "bytes4"
-			}
-		],
-		"name": "supportsInterface",
-		"outputs": [
-			{
-				"internalType": "bool",
-				"name": "",
-				"type": "bool"
-			}
-		],
-		"stateMutability": "view",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "address",
-				"name": "user",
-				"type": "address"
-			},
-			{
-				"internalType": "bool",
-				"name": "allowed",
-				"type": "bool"
-			}
-		],
-		"name": "toggleAllowed",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "bool",
-				"name": "enabled",
-				"type": "bool"
-			}
-		],
-		"name": "toggleAllowlist",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	},
-	{
-		"inputs": [
-			{
-				"internalType": "address",
-				"name": "contractAddress",
-				"type": "address"
-			},
-			{
-				"internalType": "bool",
-				"name": "enabled",
-				"type": "bool"
-			}
-		],
-		"name": "toggleSponsoring",
-		"outputs": [],
-		"stateMutability": "nonpayable",
-		"type": "function"
-	}
-]
\ No newline at end of file
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "allowed",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "allowlistEnabled",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "contractOwner",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "string", "name": "name", "type": "string" },
+      { "internalType": "string", "name": "description", "type": "string" },
+      { "internalType": "string", "name": "tokenPrefix", "type": "string" }
+    ],
+    "name": "create721Collection",
+    "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "getSponsoringRateLimit",
+    "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "uint8", "name": "mode", "type": "uint8" }
+    ],
+    "name": "setSponsoringMode",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "uint32", "name": "rateLimit", "type": "uint32" }
+    ],
+    "name": "setSponsoringRateLimit",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsoringEnabled",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      }
+    ],
+    "name": "sponsoringMode",
+    "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "bytes4", "name": "interfaceID", "type": "bytes4" }
+    ],
+    "name": "supportsInterface",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "address", "name": "user", "type": "address" },
+      { "internalType": "bool", "name": "allowed", "type": "bool" }
+    ],
+    "name": "toggleAllowed",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "bool", "name": "enabled", "type": "bool" }
+    ],
+    "name": "toggleAllowlist",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      {
+        "internalType": "address",
+        "name": "contractAddress",
+        "type": "address"
+      },
+      { "internalType": "bool", "name": "enabled", "type": "bool" }
+    ],
+    "name": "toggleSponsoring",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  }
+]