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

difftreelog

Merge pull request #776 from UniqueNetwork/feature/evm_set-get_tokenPropertyPermissions

ut-akuznetsov2022-12-16parents: #3f0dc65 #0ade15e.patch.diff
in: master
Feature/evm_set-get_tokenPropertyPermissions

22 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -6342,7 +6342,7 @@
 
 [[package]]
 name = "pallet-nonfungible"
-version = "0.1.9"
+version = "0.1.11"
 dependencies = [
  "ethereum 0.14.0",
  "evm-coder",
@@ -6501,7 +6501,7 @@
 
 [[package]]
 name = "pallet-refungible"
-version = "0.2.8"
+version = "0.2.10"
 dependencies = [
  "derivative",
  "ethereum 0.14.0",
modifiedcrates/evm-coder/tests/abi_derive_generation.rsdiffbeforeafterboth
before · crates/evm-coder/tests/abi_derive_generation.rs
1mod test_struct {2	use evm_coder_procedural::AbiCoder;3	use evm_coder::types::bytes;45	#[test]6	fn empty_struct() {7		let t = trybuild::TestCases::new();8		t.compile_fail("tests/build_failed/abi_derive_struct_generation.rs");9	}1011	#[derive(AbiCoder, PartialEq, Debug)]12	struct TypeStruct1SimpleParam {13		_a: u8,14	}1516	#[derive(AbiCoder, PartialEq, Debug)]17	struct TypeStruct1DynamicParam {18		_a: String,19	}2021	#[derive(AbiCoder, PartialEq, Debug)]22	struct TypeStruct2SimpleParam {23		_a: u8,24		_b: u32,25	}2627	#[derive(AbiCoder, PartialEq, Debug)]28	struct TypeStruct2DynamicParam {29		_a: String,30		_b: bytes,31	}3233	#[derive(AbiCoder, PartialEq, Debug)]34	struct TypeStruct2MixedParam {35		_a: u8,36		_b: bytes,37	}3839	#[derive(AbiCoder, PartialEq, Debug)]40	struct TypeStruct1DerivedSimpleParam {41		_a: TypeStruct1SimpleParam,42	}4344	#[derive(AbiCoder, PartialEq, Debug)]45	struct TypeStruct2DerivedSimpleParam {46		_a: TypeStruct1SimpleParam,47		_b: TypeStruct2SimpleParam,48	}4950	#[derive(AbiCoder, PartialEq, Debug)]51	struct TypeStruct1DerivedDynamicParam {52		_a: TypeStruct1DynamicParam,53	}5455	#[derive(AbiCoder, PartialEq, Debug)]56	struct TypeStruct2DerivedDynamicParam {57		_a: TypeStruct1DynamicParam,58		_b: TypeStruct2DynamicParam,59	}6061	/// Some docs62	/// At multi63	/// line64	#[derive(AbiCoder, PartialEq, Debug)]65	struct TypeStruct3DerivedMixedParam {66		/// Docs for A67		/// multi68		/// line69		_a: TypeStruct1SimpleParam,70		/// Docs for B71		_b: TypeStruct2DynamicParam,72		/// Docs for C73		_c: TypeStruct2MixedParam,74	}7576	#[test]77	#[cfg(feature = "stubgen")]78	fn struct_collect_type_struct3_derived_mixed_param() {79		assert_eq!(80			<TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),81			"TypeStruct3DerivedMixedParam"82		);83		similar_asserts::assert_eq!(84			<TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),85			r#"/// @dev Some docs86/// At multi87/// line88struct TypeStruct3DerivedMixedParam {89	/// @dev Docs for A90	/// multi91	/// line92	TypeStruct1SimpleParam _a;93	/// @dev Docs for B94	TypeStruct2DynamicParam _b;95	/// @dev Docs for C96	TypeStruct2MixedParam _c;97}98"#99		);100	}101102	#[test]103	fn impl_abi_type_signature() {104		assert_eq!(105			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE106				.as_str()107				.unwrap(),108			"(uint8)"109		);110		assert_eq!(111			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE112				.as_str()113				.unwrap(),114			"(string)"115		);116		assert_eq!(117			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE118				.as_str()119				.unwrap(),120			"(uint8,uint32)"121		);122		assert_eq!(123			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE124				.as_str()125				.unwrap(),126			"(string,bytes)"127		);128		assert_eq!(129			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE130				.as_str()131				.unwrap(),132			"(uint8,bytes)"133		);134		assert_eq!(135			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE136				.as_str()137				.unwrap(),138			"((uint8))"139		);140		assert_eq!(141			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE142				.as_str()143				.unwrap(),144			"((uint8),(uint8,uint32))"145		);146		assert_eq!(147			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE148				.as_str()149				.unwrap(),150			"((string))"151		);152		assert_eq!(153			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE154				.as_str()155				.unwrap(),156			"((string),(string,bytes))"157		);158		assert_eq!(159			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE160				.as_str()161				.unwrap(),162			"((uint8),(string,bytes),(uint8,bytes))"163		);164	}165166	#[test]167	fn impl_abi_type_is_dynamic() {168		assert_eq!(169			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),170			false171		);172		assert_eq!(173			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),174			true175		);176		assert_eq!(177			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),178			false179		);180		assert_eq!(181			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),182			true183		);184		assert_eq!(185			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),186			true187		);188		assert_eq!(189			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),190			false191		);192		assert_eq!(193			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),194			false195		);196		assert_eq!(197			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),198			true199		);200		assert_eq!(201			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),202			true203		);204		assert_eq!(205			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),206			true207		);208	}209210	#[test]211	fn impl_abi_type_size() {212		const ABI_ALIGNMENT: usize = 32;213		assert_eq!(214			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),215			ABI_ALIGNMENT216		);217		assert_eq!(218			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),219			ABI_ALIGNMENT220		);221		assert_eq!(222			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),223			ABI_ALIGNMENT * 2224		);225		assert_eq!(226			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),227			ABI_ALIGNMENT * 2228		);229		assert_eq!(230			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),231			ABI_ALIGNMENT * 2232		);233		assert_eq!(234			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),235			ABI_ALIGNMENT236		);237		assert_eq!(238			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),239			ABI_ALIGNMENT * 3240		);241		assert_eq!(242			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),243			ABI_ALIGNMENT244		);245		assert_eq!(246			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),247			ABI_ALIGNMENT * 3248		);249		assert_eq!(250			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),251			ABI_ALIGNMENT * 5252		);253	}254255	#[derive(AbiCoder, PartialEq, Debug)]256	struct TupleStruct1SimpleParam(u8);257258	#[derive(AbiCoder, PartialEq, Debug)]259	struct TupleStruct1DynamicParam(String);260261	#[derive(AbiCoder, PartialEq, Debug)]262	struct TupleStruct2SimpleParam(u8, u32);263264	#[derive(AbiCoder, PartialEq, Debug)]265	struct TupleStruct2DynamicParam(String, bytes);266267	#[derive(AbiCoder, PartialEq, Debug)]268	struct TupleStruct2MixedParam(u8, bytes);269270	#[derive(AbiCoder, PartialEq, Debug)]271	struct TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam);272273	#[derive(AbiCoder, PartialEq, Debug)]274	struct TupleStruct2DerivedSimpleParam(TupleStruct1SimpleParam, TupleStruct2SimpleParam);275276	#[derive(AbiCoder, PartialEq, Debug)]277	struct TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam);278279	#[derive(AbiCoder, PartialEq, Debug)]280	struct TupleStruct2DerivedDynamicParam(TupleStruct1DynamicParam, TupleStruct2DynamicParam);281282	/// Some docs283	/// At multi284	/// line285	#[derive(AbiCoder, PartialEq, Debug)]286	struct TupleStruct3DerivedMixedParam(287		/// Docs for A288		/// multi289		/// line290		TupleStruct1SimpleParam,291		TupleStruct2DynamicParam,292		/// Docs for C293		TupleStruct2MixedParam,294	);295296	#[test]297	#[cfg(feature = "stubgen")]298	fn struct_collect_tuple_struct3_derived_mixed_param() {299		assert_eq!(300			<TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),301			"TupleStruct3DerivedMixedParam"302		);303		similar_asserts::assert_eq!(304			<TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),305			r#"/// @dev Some docs306/// At multi307/// line308struct TupleStruct3DerivedMixedParam {309	/// @dev Docs for A310	/// multi311	/// line312	TupleStruct1SimpleParam field0;313	TupleStruct2DynamicParam field1;314	/// @dev Docs for C315	TupleStruct2MixedParam field2;316}317"#318		);319	}320321	#[test]322	fn impl_abi_type_signature_same_for_structs() {323		assert_eq!(324			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE325				.as_str()326				.unwrap(),327			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE328				.as_str()329				.unwrap()330		);331		assert_eq!(332			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE333				.as_str()334				.unwrap(),335			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE336				.as_str()337				.unwrap()338		);339		assert_eq!(340			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE341				.as_str()342				.unwrap(),343			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE344				.as_str()345				.unwrap()346		);347		assert_eq!(348			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE349				.as_str()350				.unwrap(),351			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE352				.as_str()353				.unwrap()354		);355		assert_eq!(356			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE357				.as_str()358				.unwrap(),359			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE360				.as_str()361				.unwrap(),362		);363		assert_eq!(364			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE365				.as_str()366				.unwrap(),367			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE368				.as_str()369				.unwrap(),370		);371		assert_eq!(372			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE373				.as_str()374				.unwrap(),375			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE376				.as_str()377				.unwrap(),378		);379		assert_eq!(380			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE381				.as_str()382				.unwrap(),383			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE384				.as_str()385				.unwrap(),386		);387		assert_eq!(388			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE389				.as_str()390				.unwrap(),391			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE392				.as_str()393				.unwrap(),394		);395		assert_eq!(396			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE397				.as_str()398				.unwrap(),399			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE400				.as_str()401				.unwrap(),402		);403	}404405	#[test]406	fn impl_abi_type_is_dynamic_same_for_structs() {407		assert_eq!(408			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),409			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic()410		);411		assert_eq!(412			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),413			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic()414		);415		assert_eq!(416			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),417			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic()418		);419		assert_eq!(420			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),421			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic()422		);423		assert_eq!(424			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),425			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic()426		);427		assert_eq!(428			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),429			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()430		);431		assert_eq!(432			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),433			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()434		);435		assert_eq!(436			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),437			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()438		);439		assert_eq!(440			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),441			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()442		);443		assert_eq!(444			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),445			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic()446		);447	}448449	#[test]450	fn impl_abi_type_size_same_for_structs() {451		assert_eq!(452			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),453			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::size()454		);455		assert_eq!(456			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),457			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::size()458		);459		assert_eq!(460			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),461			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::size()462		);463		assert_eq!(464			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),465			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::size()466		);467		assert_eq!(468			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),469			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::size()470		);471		assert_eq!(472			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),473			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size()474		);475		assert_eq!(476			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),477			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size()478		);479		assert_eq!(480			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),481			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size()482		);483		assert_eq!(484			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),485			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size()486		);487		assert_eq!(488			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),489			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size()490		);491	}492493	const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;494495	fn test_impl<Tuple, TupleStruct, TypeStruct>(496		tuple_data: Tuple,497		tuple_struct_data: TupleStruct,498		type_struct_data: TypeStruct,499	) where500		TypeStruct: evm_coder::abi::AbiWrite501			+ evm_coder::abi::AbiRead502			+ std::cmp::PartialEq503			+ std::fmt::Debug,504		TupleStruct: evm_coder::abi::AbiWrite505			+ evm_coder::abi::AbiRead506			+ std::cmp::PartialEq507			+ std::fmt::Debug,508		Tuple: evm_coder::abi::AbiWrite509			+ evm_coder::abi::AbiRead510			+ std::cmp::PartialEq511			+ std::fmt::Debug,512	{513		let encoded_type_struct = test_abi_write_impl(&type_struct_data);514		let encoded_tuple_struct = test_abi_write_impl(&tuple_struct_data);515		let encoded_tuple = test_abi_write_impl(&tuple_data);516517		similar_asserts::assert_eq!(encoded_tuple, encoded_type_struct);518		similar_asserts::assert_eq!(encoded_tuple, encoded_tuple_struct);519520		{521			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();522			let restored_struct_data = <TypeStruct>::abi_read(&mut decoder).unwrap();523			assert_eq!(restored_struct_data, type_struct_data);524		}525		{526			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();527			let restored_struct_data = <TupleStruct>::abi_read(&mut decoder).unwrap();528			assert_eq!(restored_struct_data, tuple_struct_data);529		}530531		{532			let (_, mut decoder) =533				evm_coder::abi::AbiReader::new_call(&encoded_type_struct).unwrap();534			let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();535			assert_eq!(restored_tuple_data, tuple_data);536		}537		{538			let (_, mut decoder) =539				evm_coder::abi::AbiReader::new_call(&encoded_tuple_struct).unwrap();540			let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();541			assert_eq!(restored_tuple_data, tuple_data);542		}543	}544545	fn test_abi_write_impl<A>(data: &A) -> Vec<u8>546	where547		A: evm_coder::abi::AbiWrite548			+ evm_coder::abi::AbiRead549			+ std::cmp::PartialEq550			+ std::fmt::Debug,551	{552		let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);553		data.abi_write(&mut writer);554		let encoded_tuple = writer.finish();555		encoded_tuple556	}557558	#[test]559	fn codec_struct_1_simple() {560		let _a = 0xff;561		test_impl::<(u8,), TupleStruct1SimpleParam, TypeStruct1SimpleParam>(562			(_a,),563			TupleStruct1SimpleParam(_a),564			TypeStruct1SimpleParam { _a },565		);566	}567568	#[test]569	fn codec_struct_1_dynamic() {570		let _a: String = "some string".into();571		test_impl::<(String,), TupleStruct1DynamicParam, TypeStruct1DynamicParam>(572			(_a.clone(),),573			TupleStruct1DynamicParam(_a.clone()),574			TypeStruct1DynamicParam { _a },575		);576	}577578	#[test]579	fn codec_struct_1_derived_simple() {580		let _a: u8 = 0xff;581		test_impl::<((u8,),), TupleStruct1DerivedSimpleParam, TypeStruct1DerivedSimpleParam>(582			((_a,),),583			TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam(_a)),584			TypeStruct1DerivedSimpleParam {585				_a: TypeStruct1SimpleParam { _a },586			},587		);588	}589590	#[test]591	fn codec_struct_1_derived_dynamic() {592		let _a: String = "some string".into();593		test_impl::<((String,),), TupleStruct1DerivedDynamicParam, TypeStruct1DerivedDynamicParam>(594			((_a.clone(),),),595			TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam(_a.clone())),596			TypeStruct1DerivedDynamicParam {597				_a: TypeStruct1DynamicParam { _a },598			},599		);600	}601602	#[test]603	fn codec_struct_2_simple() {604		let _a = 0xff;605		let _b = 0xbeefbaba;606		test_impl::<(u8, u32), TupleStruct2SimpleParam, TypeStruct2SimpleParam>(607			(_a, _b),608			TupleStruct2SimpleParam(_a, _b),609			TypeStruct2SimpleParam { _a, _b },610		);611	}612613	#[test]614	fn codec_struct_2_dynamic() {615		let _a: String = "some string".into();616		let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);617		test_impl::<(String, bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(618			(_a.clone(), _b.clone()),619			TupleStruct2DynamicParam(_a.clone(), _b.clone()),620			TypeStruct2DynamicParam { _a, _b },621		);622	}623624	#[test]625	fn codec_struct_2_mixed() {626		let _a: u8 = 0xff;627		let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);628		test_impl::<(u8, bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(629			(_a.clone(), _b.clone()),630			TupleStruct2MixedParam(_a.clone(), _b.clone()),631			TypeStruct2MixedParam { _a, _b },632		);633	}634635	#[test]636	fn codec_struct_2_derived_simple() {637		let _a = 0xff;638		let _b = 0xbeefbaba;639		test_impl::<640			((u8,), (u8, u32)),641			TupleStruct2DerivedSimpleParam,642			TypeStruct2DerivedSimpleParam,643		>(644			((_a,), (_a, _b)),645			TupleStruct2DerivedSimpleParam(646				TupleStruct1SimpleParam(_a),647				TupleStruct2SimpleParam(_a, _b),648			),649			TypeStruct2DerivedSimpleParam {650				_a: TypeStruct1SimpleParam { _a },651				_b: TypeStruct2SimpleParam { _a, _b },652			},653		);654	}655656	#[test]657	fn codec_struct_2_derived_dynamic() {658		let _a = "some string".to_string();659		let _b = bytes(vec![0x11, 0x22, 0x33]);660		test_impl::<661			((String,), (String, bytes)),662			TupleStruct2DerivedDynamicParam,663			TypeStruct2DerivedDynamicParam,664		>(665			((_a.clone(),), (_a.clone(), _b.clone())),666			TupleStruct2DerivedDynamicParam(667				TupleStruct1DynamicParam(_a.clone()),668				TupleStruct2DynamicParam(_a.clone(), _b.clone()),669			),670			TypeStruct2DerivedDynamicParam {671				_a: TypeStruct1DynamicParam { _a: _a.clone() },672				_b: TypeStruct2DynamicParam { _a, _b },673			},674		);675	}676677	#[test]678	fn codec_struct_3_derived_mixed() {679		let int = 0xff;680		let by = bytes(vec![0x11, 0x22, 0x33]);681		let string = "some string".to_string();682		test_impl::<683			((u8,), (String, bytes), (u8, bytes)),684			TupleStruct3DerivedMixedParam,685			TypeStruct3DerivedMixedParam,686		>(687			((int,), (string.clone(), by.clone()), (int, by.clone())),688			TupleStruct3DerivedMixedParam(689				TupleStruct1SimpleParam(int),690				TupleStruct2DynamicParam(string.clone(), by.clone()),691				TupleStruct2MixedParam(int, by.clone()),692			),693			TypeStruct3DerivedMixedParam {694				_a: TypeStruct1SimpleParam { _a: int },695				_b: TypeStruct2DynamicParam {696					_a: string.clone(),697					_b: by.clone(),698				},699				_c: TypeStruct2MixedParam { _a: int, _b: by },700			},701		);702	}703704	#[derive(AbiCoder, PartialEq, Debug)]705	struct TypeStruct2SimpleStruct1Simple {706		_a: TypeStruct2SimpleParam,707		_b: TypeStruct2SimpleParam,708		_c: u8,709	}710	#[derive(AbiCoder, PartialEq, Debug)]711	struct TupleStruct2SimpleStruct1Simple(TupleStruct2SimpleParam, TupleStruct2SimpleParam, u8);712713	#[test]714	fn codec_struct_2_struct_simple_1_simple() {715		let _a = 0xff;716		let _b = 0xbeefbaba;717		test_impl::<718			((u8, u32), (u8, u32), u8),719			TupleStruct2SimpleStruct1Simple,720			TypeStruct2SimpleStruct1Simple,721		>(722			((_a, _b), (_a, _b), _a),723			TupleStruct2SimpleStruct1Simple(724				TupleStruct2SimpleParam(_a, _b),725				TupleStruct2SimpleParam(_a, _b),726				_a,727			),728			TypeStruct2SimpleStruct1Simple {729				_a: TypeStruct2SimpleParam { _a, _b },730				_b: TypeStruct2SimpleParam { _a, _b },731				_c: _a,732			},733		);734	}735}736737mod test_enum {738	use evm_coder::AbiCoder;739740	/// Some docs741	/// At multi742	/// line743	#[derive(AbiCoder, Debug, PartialEq, Default)]744	#[repr(u8)]745	enum Color {746		/// Docs for Red747		/// multi748		/// line749		Red,750		Green,751		/// Docs for Blue752		#[default]753		Blue,754	}755756	#[test]757	fn empty() {}758759	#[test]760	fn bad_enums() {761		let t = trybuild::TestCases::new();762		t.compile_fail("tests/build_failed/abi_derive_enum_generation.rs");763	}764765	#[test]766	fn impl_abi_type_signature_same_for_structs() {767		assert_eq!(768			<Color as evm_coder::abi::AbiType>::SIGNATURE769				.as_str()770				.unwrap(),771			<u8 as evm_coder::abi::AbiType>::SIGNATURE.as_str().unwrap()772		);773	}774775	#[test]776	fn impl_abi_type_is_dynamic_same_for_structs() {777		assert_eq!(778			<Color as evm_coder::abi::AbiType>::is_dynamic(),779			<u8 as evm_coder::abi::AbiType>::is_dynamic()780		);781	}782783	#[test]784	fn impl_abi_type_size_same_for_structs() {785		assert_eq!(786			<Color as evm_coder::abi::AbiType>::size(),787			<u8 as evm_coder::abi::AbiType>::size()788		);789	}790791	#[test]792	fn test_coder() {793		const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;794795		let encoded_enum = {796			let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);797			<Color as evm_coder::abi::AbiWrite>::abi_write(&Color::Green, &mut writer);798			writer.finish()799		};800801		let encoded_u8 = {802			let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);803			<u8 as evm_coder::abi::AbiWrite>::abi_write(&(Color::Green as u8), &mut writer);804			writer.finish()805		};806807		similar_asserts::assert_eq!(encoded_enum, encoded_u8);808809		{810			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_enum).unwrap();811			let restored_enum_data =812				<Color as evm_coder::abi::AbiRead>::abi_read(&mut decoder).unwrap();813			assert_eq!(restored_enum_data, Color::Green);814		}815	}816817	#[test]818	#[cfg(feature = "stubgen")]819	fn struct_collect_enum() {820		assert_eq!(821			<Color as ::evm_coder::solidity::StructCollect>::name(),822			"Color"823		);824		similar_asserts::assert_eq!(825			<Color as ::evm_coder::solidity::StructCollect>::declaration(),826			r#"/// @dev Some docs827/// At multi828/// line829enum Color {830	/// @dev Docs for Red831	/// multi832	/// line833	Red,834	Green,835	/// @dev Docs for Blue836	Blue837}838"#839		);840	}841}
after · crates/evm-coder/tests/abi_derive_generation.rs
1mod test_struct {2	use evm_coder_procedural::AbiCoder;3	use evm_coder::types::bytes;45	#[test]6	fn empty_struct() {7		let t = trybuild::TestCases::new();8		t.compile_fail("tests/build_failed/abi_derive_struct_generation.rs");9	}1011	#[derive(AbiCoder, PartialEq, Debug)]12	struct TypeStruct1SimpleParam {13		_a: u8,14	}1516	#[derive(AbiCoder, PartialEq, Debug)]17	struct TypeStruct1DynamicParam {18		_a: String,19	}2021	#[derive(AbiCoder, PartialEq, Debug)]22	struct TypeStruct2SimpleParam {23		_a: u8,24		_b: u32,25	}2627	#[derive(AbiCoder, PartialEq, Debug)]28	struct TypeStruct2DynamicParam {29		_a: String,30		_b: bytes,31	}3233	#[derive(AbiCoder, PartialEq, Debug)]34	struct TypeStruct2MixedParam {35		_a: u8,36		_b: bytes,37	}3839	#[derive(AbiCoder, PartialEq, Debug)]40	struct TypeStruct1DerivedSimpleParam {41		_a: TypeStruct1SimpleParam,42	}4344	#[derive(AbiCoder, PartialEq, Debug)]45	struct TypeStruct2DerivedSimpleParam {46		_a: TypeStruct1SimpleParam,47		_b: TypeStruct2SimpleParam,48	}4950	#[derive(AbiCoder, PartialEq, Debug)]51	struct TypeStruct1DerivedDynamicParam {52		_a: TypeStruct1DynamicParam,53	}5455	#[derive(AbiCoder, PartialEq, Debug)]56	struct TypeStruct2DerivedDynamicParam {57		_a: TypeStruct1DynamicParam,58		_b: TypeStruct2DynamicParam,59	}6061	/// Some docs62	/// At multi63	/// line64	#[derive(AbiCoder, PartialEq, Debug)]65	struct TypeStruct3DerivedMixedParam {66		/// Docs for A67		/// multi68		/// line69		_a: TypeStruct1SimpleParam,70		/// Docs for B71		_b: TypeStruct2DynamicParam,72		/// Docs for C73		_c: TypeStruct2MixedParam,74	}7576	#[test]77	#[cfg(feature = "stubgen")]78	fn struct_collect_type_struct3_derived_mixed_param() {79		assert_eq!(80			<TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),81			"TypeStruct3DerivedMixedParam"82		);83		similar_asserts::assert_eq!(84			<TypeStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),85			r#"/// @dev Some docs86/// At multi87/// line88struct TypeStruct3DerivedMixedParam {89	/// @dev Docs for A90	/// multi91	/// line92	TypeStruct1SimpleParam _a;93	/// @dev Docs for B94	TypeStruct2DynamicParam _b;95	/// @dev Docs for C96	TypeStruct2MixedParam _c;97}98"#99		);100	}101102	#[test]103	fn impl_abi_type_signature() {104		assert_eq!(105			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE106				.as_str()107				.unwrap(),108			"(uint8)"109		);110		assert_eq!(111			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE112				.as_str()113				.unwrap(),114			"(string)"115		);116		assert_eq!(117			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE118				.as_str()119				.unwrap(),120			"(uint8,uint32)"121		);122		assert_eq!(123			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE124				.as_str()125				.unwrap(),126			"(string,bytes)"127		);128		assert_eq!(129			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE130				.as_str()131				.unwrap(),132			"(uint8,bytes)"133		);134		assert_eq!(135			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE136				.as_str()137				.unwrap(),138			"((uint8))"139		);140		assert_eq!(141			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE142				.as_str()143				.unwrap(),144			"((uint8),(uint8,uint32))"145		);146		assert_eq!(147			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE148				.as_str()149				.unwrap(),150			"((string))"151		);152		assert_eq!(153			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE154				.as_str()155				.unwrap(),156			"((string),(string,bytes))"157		);158		assert_eq!(159			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE160				.as_str()161				.unwrap(),162			"((uint8),(string,bytes),(uint8,bytes))"163		);164	}165166	#[test]167	fn impl_abi_type_is_dynamic() {168		assert_eq!(169			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),170			false171		);172		assert_eq!(173			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),174			true175		);176		assert_eq!(177			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),178			false179		);180		assert_eq!(181			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),182			true183		);184		assert_eq!(185			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),186			true187		);188		assert_eq!(189			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),190			false191		);192		assert_eq!(193			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),194			false195		);196		assert_eq!(197			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),198			true199		);200		assert_eq!(201			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),202			true203		);204		assert_eq!(205			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),206			true207		);208	}209210	#[test]211	fn impl_abi_type_size() {212		const ABI_ALIGNMENT: usize = 32;213		assert_eq!(214			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),215			ABI_ALIGNMENT216		);217		assert_eq!(218			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),219			ABI_ALIGNMENT220		);221		assert_eq!(222			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),223			ABI_ALIGNMENT * 2224		);225		assert_eq!(226			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),227			ABI_ALIGNMENT * 2228		);229		assert_eq!(230			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),231			ABI_ALIGNMENT * 2232		);233		assert_eq!(234			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),235			ABI_ALIGNMENT236		);237		assert_eq!(238			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),239			ABI_ALIGNMENT * 3240		);241		assert_eq!(242			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),243			ABI_ALIGNMENT244		);245		assert_eq!(246			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),247			ABI_ALIGNMENT * 3248		);249		assert_eq!(250			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),251			ABI_ALIGNMENT * 5252		);253	}254255	#[derive(AbiCoder, PartialEq, Debug)]256	struct TupleStruct1SimpleParam(u8);257258	#[derive(AbiCoder, PartialEq, Debug)]259	struct TupleStruct1DynamicParam(String);260261	#[derive(AbiCoder, PartialEq, Debug)]262	struct TupleStruct2SimpleParam(u8, u32);263264	#[derive(AbiCoder, PartialEq, Debug)]265	struct TupleStruct2DynamicParam(String, bytes);266267	#[derive(AbiCoder, PartialEq, Debug)]268	struct TupleStruct2MixedParam(u8, bytes);269270	#[derive(AbiCoder, PartialEq, Debug)]271	struct TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam);272273	#[derive(AbiCoder, PartialEq, Debug)]274	struct TupleStruct2DerivedSimpleParam(TupleStruct1SimpleParam, TupleStruct2SimpleParam);275276	#[derive(AbiCoder, PartialEq, Debug)]277	struct TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam);278279	#[derive(AbiCoder, PartialEq, Debug)]280	struct TupleStruct2DerivedDynamicParam(TupleStruct1DynamicParam, TupleStruct2DynamicParam);281282	/// Some docs283	/// At multi284	/// line285	#[derive(AbiCoder, PartialEq, Debug)]286	struct TupleStruct3DerivedMixedParam(287		/// Docs for A288		/// multi289		/// line290		TupleStruct1SimpleParam,291		TupleStruct2DynamicParam,292		/// Docs for C293		TupleStruct2MixedParam,294	);295296	#[test]297	#[cfg(feature = "stubgen")]298	fn struct_collect_tuple_struct3_derived_mixed_param() {299		assert_eq!(300			<TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::name(),301			"TupleStruct3DerivedMixedParam"302		);303		similar_asserts::assert_eq!(304			<TupleStruct3DerivedMixedParam as ::evm_coder::solidity::StructCollect>::declaration(),305			r#"/// @dev Some docs306/// At multi307/// line308struct TupleStruct3DerivedMixedParam {309	/// @dev Docs for A310	/// multi311	/// line312	TupleStruct1SimpleParam field0;313	TupleStruct2DynamicParam field1;314	/// @dev Docs for C315	TupleStruct2MixedParam field2;316}317"#318		);319	}320321	#[test]322	fn impl_abi_type_signature_same_for_structs() {323		assert_eq!(324			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE325				.as_str()326				.unwrap(),327			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::SIGNATURE328				.as_str()329				.unwrap()330		);331		assert_eq!(332			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE333				.as_str()334				.unwrap(),335			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::SIGNATURE336				.as_str()337				.unwrap()338		);339		assert_eq!(340			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE341				.as_str()342				.unwrap(),343			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::SIGNATURE344				.as_str()345				.unwrap()346		);347		assert_eq!(348			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE349				.as_str()350				.unwrap(),351			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::SIGNATURE352				.as_str()353				.unwrap()354		);355		assert_eq!(356			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE357				.as_str()358				.unwrap(),359			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::SIGNATURE360				.as_str()361				.unwrap(),362		);363		assert_eq!(364			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE365				.as_str()366				.unwrap(),367			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE368				.as_str()369				.unwrap(),370		);371		assert_eq!(372			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE373				.as_str()374				.unwrap(),375			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::SIGNATURE376				.as_str()377				.unwrap(),378		);379		assert_eq!(380			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE381				.as_str()382				.unwrap(),383			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE384				.as_str()385				.unwrap(),386		);387		assert_eq!(388			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE389				.as_str()390				.unwrap(),391			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::SIGNATURE392				.as_str()393				.unwrap(),394		);395		assert_eq!(396			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE397				.as_str()398				.unwrap(),399			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::SIGNATURE400				.as_str()401				.unwrap(),402		);403	}404405	#[test]406	fn impl_abi_type_is_dynamic_same_for_structs() {407		assert_eq!(408			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),409			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::is_dynamic()410		);411		assert_eq!(412			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),413			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::is_dynamic()414		);415		assert_eq!(416			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic(),417			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::is_dynamic()418		);419		assert_eq!(420			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic(),421			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::is_dynamic()422		);423		assert_eq!(424			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic(),425			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::is_dynamic()426		);427		assert_eq!(428			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),429			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()430		);431		assert_eq!(432			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic(),433			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::is_dynamic()434		);435		assert_eq!(436			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),437			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()438		);439		assert_eq!(440			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic(),441			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::is_dynamic()442		);443		assert_eq!(444			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic(),445			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::is_dynamic()446		);447	}448449	#[test]450	fn impl_abi_type_size_same_for_structs() {451		assert_eq!(452			<TypeStruct1SimpleParam as evm_coder::abi::AbiType>::size(),453			<TupleStruct1SimpleParam as evm_coder::abi::AbiType>::size()454		);455		assert_eq!(456			<TypeStruct1DynamicParam as evm_coder::abi::AbiType>::size(),457			<TupleStruct1DynamicParam as evm_coder::abi::AbiType>::size()458		);459		assert_eq!(460			<TypeStruct2SimpleParam as evm_coder::abi::AbiType>::size(),461			<TupleStruct2SimpleParam as evm_coder::abi::AbiType>::size()462		);463		assert_eq!(464			<TypeStruct2DynamicParam as evm_coder::abi::AbiType>::size(),465			<TupleStruct2DynamicParam as evm_coder::abi::AbiType>::size()466		);467		assert_eq!(468			<TypeStruct2MixedParam as evm_coder::abi::AbiType>::size(),469			<TupleStruct2MixedParam as evm_coder::abi::AbiType>::size()470		);471		assert_eq!(472			<TypeStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size(),473			<TupleStruct1DerivedSimpleParam as evm_coder::abi::AbiType>::size()474		);475		assert_eq!(476			<TypeStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size(),477			<TupleStruct2DerivedSimpleParam as evm_coder::abi::AbiType>::size()478		);479		assert_eq!(480			<TypeStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size(),481			<TupleStruct1DerivedDynamicParam as evm_coder::abi::AbiType>::size()482		);483		assert_eq!(484			<TypeStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size(),485			<TupleStruct2DerivedDynamicParam as evm_coder::abi::AbiType>::size()486		);487		assert_eq!(488			<TypeStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size(),489			<TupleStruct3DerivedMixedParam as evm_coder::abi::AbiType>::size()490		);491	}492493	const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;494495	fn test_impl<Tuple, TupleStruct, TypeStruct>(496		tuple_data: Tuple,497		tuple_struct_data: TupleStruct,498		type_struct_data: TypeStruct,499	) where500		TypeStruct: evm_coder::abi::AbiWrite501			+ evm_coder::abi::AbiRead502			+ std::cmp::PartialEq503			+ std::fmt::Debug,504		TupleStruct: evm_coder::abi::AbiWrite505			+ evm_coder::abi::AbiRead506			+ std::cmp::PartialEq507			+ std::fmt::Debug,508		Tuple: evm_coder::abi::AbiWrite509			+ evm_coder::abi::AbiRead510			+ std::cmp::PartialEq511			+ std::fmt::Debug,512	{513		let encoded_type_struct = test_abi_write_impl(&type_struct_data);514		let encoded_tuple_struct = test_abi_write_impl(&tuple_struct_data);515		let encoded_tuple = test_abi_write_impl(&tuple_data);516517		similar_asserts::assert_eq!(encoded_tuple, encoded_type_struct);518		similar_asserts::assert_eq!(encoded_tuple, encoded_tuple_struct);519520		{521			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();522			let restored_struct_data = <TypeStruct>::abi_read(&mut decoder).unwrap();523			assert_eq!(restored_struct_data, type_struct_data);524		}525		{526			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_tuple).unwrap();527			let restored_struct_data = <TupleStruct>::abi_read(&mut decoder).unwrap();528			assert_eq!(restored_struct_data, tuple_struct_data);529		}530531		{532			let (_, mut decoder) =533				evm_coder::abi::AbiReader::new_call(&encoded_type_struct).unwrap();534			let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();535			assert_eq!(restored_tuple_data, tuple_data);536		}537		{538			let (_, mut decoder) =539				evm_coder::abi::AbiReader::new_call(&encoded_tuple_struct).unwrap();540			let restored_tuple_data = <Tuple>::abi_read(&mut decoder).unwrap();541			assert_eq!(restored_tuple_data, tuple_data);542		}543	}544545	fn test_abi_write_impl<A>(data: &A) -> Vec<u8>546	where547		A: evm_coder::abi::AbiWrite548			+ evm_coder::abi::AbiRead549			+ std::cmp::PartialEq550			+ std::fmt::Debug,551	{552		let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);553		data.abi_write(&mut writer);554		let encoded_tuple = writer.finish();555		encoded_tuple556	}557558	#[test]559	fn codec_struct_1_simple() {560		let _a = 0xff;561		test_impl::<(u8,), TupleStruct1SimpleParam, TypeStruct1SimpleParam>(562			(_a,),563			TupleStruct1SimpleParam(_a),564			TypeStruct1SimpleParam { _a },565		);566	}567568	#[test]569	fn codec_struct_1_dynamic() {570		let _a: String = "some string".into();571		test_impl::<(String,), TupleStruct1DynamicParam, TypeStruct1DynamicParam>(572			(_a.clone(),),573			TupleStruct1DynamicParam(_a.clone()),574			TypeStruct1DynamicParam { _a },575		);576	}577578	#[test]579	fn codec_struct_1_derived_simple() {580		let _a: u8 = 0xff;581		test_impl::<((u8,),), TupleStruct1DerivedSimpleParam, TypeStruct1DerivedSimpleParam>(582			((_a,),),583			TupleStruct1DerivedSimpleParam(TupleStruct1SimpleParam(_a)),584			TypeStruct1DerivedSimpleParam {585				_a: TypeStruct1SimpleParam { _a },586			},587		);588	}589590	#[test]591	fn codec_struct_1_derived_dynamic() {592		let _a: String = "some string".into();593		test_impl::<((String,),), TupleStruct1DerivedDynamicParam, TypeStruct1DerivedDynamicParam>(594			((_a.clone(),),),595			TupleStruct1DerivedDynamicParam(TupleStruct1DynamicParam(_a.clone())),596			TypeStruct1DerivedDynamicParam {597				_a: TypeStruct1DynamicParam { _a },598			},599		);600	}601602	#[test]603	fn codec_struct_2_simple() {604		let _a = 0xff;605		let _b = 0xbeefbaba;606		test_impl::<(u8, u32), TupleStruct2SimpleParam, TypeStruct2SimpleParam>(607			(_a, _b),608			TupleStruct2SimpleParam(_a, _b),609			TypeStruct2SimpleParam { _a, _b },610		);611	}612613	#[test]614	fn codec_struct_2_dynamic() {615		let _a: String = "some string".into();616		let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);617		test_impl::<(String, bytes), TupleStruct2DynamicParam, TypeStruct2DynamicParam>(618			(_a.clone(), _b.clone()),619			TupleStruct2DynamicParam(_a.clone(), _b.clone()),620			TypeStruct2DynamicParam { _a, _b },621		);622	}623624	#[test]625	fn codec_struct_2_mixed() {626		let _a: u8 = 0xff;627		let _b: bytes = bytes(vec![0x11, 0x22, 0x33]);628		test_impl::<(u8, bytes), TupleStruct2MixedParam, TypeStruct2MixedParam>(629			(_a.clone(), _b.clone()),630			TupleStruct2MixedParam(_a.clone(), _b.clone()),631			TypeStruct2MixedParam { _a, _b },632		);633	}634635	#[test]636	fn codec_struct_2_derived_simple() {637		let _a = 0xff;638		let _b = 0xbeefbaba;639		test_impl::<640			((u8,), (u8, u32)),641			TupleStruct2DerivedSimpleParam,642			TypeStruct2DerivedSimpleParam,643		>(644			((_a,), (_a, _b)),645			TupleStruct2DerivedSimpleParam(646				TupleStruct1SimpleParam(_a),647				TupleStruct2SimpleParam(_a, _b),648			),649			TypeStruct2DerivedSimpleParam {650				_a: TypeStruct1SimpleParam { _a },651				_b: TypeStruct2SimpleParam { _a, _b },652			},653		);654	}655656	#[test]657	fn codec_struct_2_derived_dynamic() {658		let _a = "some string".to_string();659		let _b = bytes(vec![0x11, 0x22, 0x33]);660		test_impl::<661			((String,), (String, bytes)),662			TupleStruct2DerivedDynamicParam,663			TypeStruct2DerivedDynamicParam,664		>(665			((_a.clone(),), (_a.clone(), _b.clone())),666			TupleStruct2DerivedDynamicParam(667				TupleStruct1DynamicParam(_a.clone()),668				TupleStruct2DynamicParam(_a.clone(), _b.clone()),669			),670			TypeStruct2DerivedDynamicParam {671				_a: TypeStruct1DynamicParam { _a: _a.clone() },672				_b: TypeStruct2DynamicParam { _a, _b },673			},674		);675	}676677	#[test]678	fn codec_struct_3_derived_mixed() {679		let int = 0xff;680		let by = bytes(vec![0x11, 0x22, 0x33]);681		let string = "some string".to_string();682		test_impl::<683			((u8,), (String, bytes), (u8, bytes)),684			TupleStruct3DerivedMixedParam,685			TypeStruct3DerivedMixedParam,686		>(687			((int,), (string.clone(), by.clone()), (int, by.clone())),688			TupleStruct3DerivedMixedParam(689				TupleStruct1SimpleParam(int),690				TupleStruct2DynamicParam(string.clone(), by.clone()),691				TupleStruct2MixedParam(int, by.clone()),692			),693			TypeStruct3DerivedMixedParam {694				_a: TypeStruct1SimpleParam { _a: int },695				_b: TypeStruct2DynamicParam {696					_a: string.clone(),697					_b: by.clone(),698				},699				_c: TypeStruct2MixedParam { _a: int, _b: by },700			},701		);702	}703704	#[derive(AbiCoder, PartialEq, Debug)]705	struct TypeStruct2SimpleStruct1Simple {706		_a: TypeStruct2SimpleParam,707		_b: TypeStruct2SimpleParam,708		_c: u8,709	}710	#[derive(AbiCoder, PartialEq, Debug)]711	struct TupleStruct2SimpleStruct1Simple(TupleStruct2SimpleParam, TupleStruct2SimpleParam, u8);712713	#[test]714	fn codec_struct_2_struct_simple_1_simple() {715		let _a = 0xff;716		let _b = 0xbeefbaba;717		test_impl::<718			((u8, u32), (u8, u32), u8),719			TupleStruct2SimpleStruct1Simple,720			TypeStruct2SimpleStruct1Simple,721		>(722			((_a, _b), (_a, _b), _a),723			TupleStruct2SimpleStruct1Simple(724				TupleStruct2SimpleParam(_a, _b),725				TupleStruct2SimpleParam(_a, _b),726				_a,727			),728			TypeStruct2SimpleStruct1Simple {729				_a: TypeStruct2SimpleParam { _a, _b },730				_b: TypeStruct2SimpleParam { _a, _b },731				_c: _a,732			},733		);734	}735}736737mod test_enum {738	use evm_coder::AbiCoder;739740	/// Some docs741	/// At multi742	/// line743	#[derive(AbiCoder, Debug, PartialEq, Default, Clone, Copy)]744	#[repr(u8)]745	enum Color {746		/// Docs for Red747		/// multi748		/// line749		Red,750		Green,751		/// Docs for Blue752		#[default]753		Blue,754	}755756	#[test]757	fn empty() {}758759	#[test]760	fn bad_enums() {761		let t = trybuild::TestCases::new();762		t.compile_fail("tests/build_failed/abi_derive_enum_generation.rs");763	}764765	#[test]766	fn impl_abi_type_signature_same_for_structs() {767		assert_eq!(768			<Color as evm_coder::abi::AbiType>::SIGNATURE769				.as_str()770				.unwrap(),771			<u8 as evm_coder::abi::AbiType>::SIGNATURE.as_str().unwrap()772		);773	}774775	#[test]776	fn impl_abi_type_is_dynamic_same_for_structs() {777		assert_eq!(778			<Color as evm_coder::abi::AbiType>::is_dynamic(),779			<u8 as evm_coder::abi::AbiType>::is_dynamic()780		);781	}782783	#[test]784	fn impl_abi_type_size_same_for_structs() {785		assert_eq!(786			<Color as evm_coder::abi::AbiType>::size(),787			<u8 as evm_coder::abi::AbiType>::size()788		);789	}790791	#[test]792	fn test_coder() {793		const FUNCTION_IDENTIFIER: u32 = 0xdeadbeef;794795		let encoded_enum = {796			let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);797			<Color as evm_coder::abi::AbiWrite>::abi_write(&Color::Green, &mut writer);798			writer.finish()799		};800801		let encoded_u8 = {802			let mut writer = evm_coder::abi::AbiWriter::new_call(FUNCTION_IDENTIFIER);803			<u8 as evm_coder::abi::AbiWrite>::abi_write(&(Color::Green as u8), &mut writer);804			writer.finish()805		};806807		similar_asserts::assert_eq!(encoded_enum, encoded_u8);808809		{810			let (_, mut decoder) = evm_coder::abi::AbiReader::new_call(&encoded_enum).unwrap();811			let restored_enum_data =812				<Color as evm_coder::abi::AbiRead>::abi_read(&mut decoder).unwrap();813			assert_eq!(restored_enum_data, Color::Green);814		}815	}816817	#[test]818	#[cfg(feature = "stubgen")]819	fn struct_collect_enum() {820		assert_eq!(821			<Color as ::evm_coder::solidity::StructCollect>::name(),822			"Color"823		);824		similar_asserts::assert_eq!(825			<Color as ::evm_coder::solidity::StructCollect>::declaration(),826			r#"/// @dev Some docs827/// At multi828/// line829enum Color {830	/// @dev Docs for Red831	/// multi832	/// line833	Red,834	Green,835	/// @dev Docs for Blue836	Blue837}838"#839		);840	}841}
modifiedpallets/common/src/eth.rsdiffbeforeafterboth
--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -162,3 +162,18 @@
 	CollectionAdmin,
 	TokenOwner,
 }
+
+/// Ethereum representation of TokenPermissions (see [`up_data_structs::PropertyPermission`]) fields as an enumeration.
+#[derive(AbiCoder, Copy, Clone, Default, Debug)]
+#[repr(u8)]
+pub enum EthTokenPermissions {
+	/// Permission to change the property and property permission. See [`up_data_structs::PropertyPermission::mutable`]
+	#[default]
+	Mutable,
+
+	/// Change permission for the collection administrator. See [`up_data_structs::PropertyPermission::token_owner`]
+	TokenOwner,
+
+	/// Permission to change the property for the owner of the token. See [`up_data_structs::PropertyPermission::collection_admin`]
+	CollectionAdmin,
+}
modifiedpallets/nonfungible/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/nonfungible/CHANGELOG.md
+++ b/pallets/nonfungible/CHANGELOG.md
@@ -4,6 +4,16 @@
 
 <!-- bureaucrate goes here -->
 
+## [0.1.11] - 2022-12-16
+
+### Added
+
+- The function `tokenPropertyPermissions` and `setTokenPropertyPermissions` to `TokenProperties` interface.
+
+### Changed
+
+- Hide `setTokenPropertyPermission` function in `TokenProperties` interface.
+
 ## [0.1.10] - 2022-11-18
 
 ### Added
modifiedpallets/nonfungible/Cargo.tomldiffbeforeafterboth
--- a/pallets/nonfungible/Cargo.toml
+++ b/pallets/nonfungible/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pallet-nonfungible"
-version = "0.1.9"
+version = "0.1.11"
 license = "GPLv3"
 edition = "2021"
 
modifiedpallets/nonfungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -34,11 +34,11 @@
 	CollectionPropertiesVec,
 };
 use pallet_evm_coder_substrate::dispatch_to_evm;
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, vec};
 use pallet_common::{
 	CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,
 	erc::{CommonEvmHandler, PrecompileResult, CollectionCall, static_property::key},
-	eth::EthCrossAccount,
+	eth::{EthCrossAccount, EthTokenPermissions},
 };
 use pallet_evm::{account::CrossAccountId, PrecompileHandle};
 use pallet_evm_coder_substrate::call;
@@ -60,6 +60,7 @@
 	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
 	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
 	#[weight(<SelfWeightOf<T>>::set_token_property_permissions(1))]
+	#[solidity(hide)]
 	fn set_token_property_permission(
 		&mut self,
 		caller: caller,
@@ -69,10 +70,10 @@
 		token_owner: bool,
 	) -> Result<()> {
 		let caller = T::CrossAccountId::from_eth(caller);
-		<Pallet<T>>::set_property_permission(
+		<Pallet<T>>::set_token_property_permissions(
 			self,
 			&caller,
-			PropertyKeyPermission {
+			vec![PropertyKeyPermission {
 				key: <Vec<u8>>::from(key)
 					.try_into()
 					.map_err(|_| "too long key")?,
@@ -81,11 +82,78 @@
 					collection_admin,
 					token_owner,
 				},
-			},
+			}],
 		)
 		.map_err(dispatch_to_evm::<T>)
 	}
 
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param permissions Permissions for keys.
+	#[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]
+	fn set_token_property_permissions(
+		&mut self,
+		caller: caller,
+		permissions: Vec<(string, Vec<(EthTokenPermissions, bool)>)>,
+	) -> Result<()> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		const PERMISSIONS_FIELDS_COUNT: usize = 3;
+
+		let mut perms = Vec::new();
+
+		for (key, pp) in permissions {
+			if pp.len() > PERMISSIONS_FIELDS_COUNT {
+				return Err(alloc::format!(
+					"Actual number of fields {} for {}, which exceeds the maximum value of {}",
+					pp.len(),
+					stringify!(EthTokenPermissions),
+					PERMISSIONS_FIELDS_COUNT
+				)
+				.as_str()
+				.into());
+			}
+
+			let mut token_permission = PropertyPermission::default();
+
+			for (perm, value) in pp {
+				match perm {
+					EthTokenPermissions::Mutable => token_permission.mutable = value,
+					EthTokenPermissions::TokenOwner => token_permission.token_owner = value,
+					EthTokenPermissions::CollectionAdmin => {
+						token_permission.collection_admin = value
+					}
+				}
+			}
+
+			perms.push(PropertyKeyPermission {
+				key: key.into_bytes().try_into().map_err(|_| "too long key")?,
+				permission: token_permission,
+			});
+		}
+
+		<Pallet<T>>::set_token_property_permissions(self, &caller, perms)
+			.map_err(dispatch_to_evm::<T>)
+	}
+
+	/// @notice Get permissions for token properties.
+	fn token_property_permissions(
+		&self,
+	) -> Result<Vec<(string, Vec<(EthTokenPermissions, bool)>)>> {
+		let perms = <Pallet<T>>::token_property_permission(self.id);
+		Ok(perms
+			.into_iter()
+			.map(|(key, pp)| {
+				let key = string::from_utf8(key.into_inner()).expect("Stored key must be valid");
+				let pp = vec![
+					(EthTokenPermissions::Mutable, pp.mutable),
+					(EthTokenPermissions::TokenOwner, pp.token_owner),
+					(EthTokenPermissions::CollectionAdmin, pp.collection_admin),
+				];
+				(key, pp)
+			})
+			.collect())
+	}
+
 	/// @notice Set token property value.
 	/// @dev Throws error if `msg.sender` has no permission to edit the property.
 	/// @param tokenId ID of the token.
modifiedpallets/nonfungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -103,7 +103,7 @@
 	AccessMode, CollectionId, CollectionFlags, CustomDataLimit, TokenId, CreateCollectionData,
 	CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission,
 	PropertyKey, PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty,
-	TokenChild, AuxPropertyValue,
+	TokenChild, AuxPropertyValue, PropertiesPermissionMap,
 };
 use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
 use pallet_common::{
@@ -824,15 +824,8 @@
 		)
 	}
 
-	/// Set property permissions for the collection.
-	///
-	/// Sender should be the owner or admin of the collection.
-	pub fn set_property_permission(
-		collection: &CollectionHandle<T>,
-		sender: &T::CrossAccountId,
-		permission: PropertyKeyPermission,
-	) -> DispatchResult {
-		<PalletCommon<T>>::set_property_permission(collection, sender, permission)
+	pub fn token_property_permission(collection_id: CollectionId) -> PropertiesPermissionMap {
+		<PalletCommon<T>>::property_permissions(collection_id)
 	}
 
 	pub fn check_token_immediate_ownership(
modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -18,30 +18,44 @@
 }
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0x91a97a68
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
 contract TokenProperties is Dummy, ERC165 {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) public {
+	// 	require(false, stub_error);
+	// 	key;
+	// 	isMutable;
+	// 	collectionAdmin;
+	// 	tokenOwner;
+	// 	dummy = 0;
+	// }
+
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param key Property key.
-	/// @param isMutable Permission to mutate property.
-	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	/// @dev EVM selector for this function is: 0x222d97fa,
-	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	function setTokenPropertyPermission(
-		string memory key,
-		bool isMutable,
-		bool collectionAdmin,
-		bool tokenOwner
-	) public {
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(Tuple48[] memory permissions) public {
 		require(false, stub_error);
-		key;
-		isMutable;
-		collectionAdmin;
-		tokenOwner;
+		permissions;
 		dummy = 0;
 	}
 
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() public view returns (Tuple48[] memory) {
+		require(false, stub_error);
+		dummy;
+		return new Tuple48[](0);
+	}
+
 	// /// @notice Set token property value.
 	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
 	// /// @param tokenId ID of the token.
@@ -118,6 +132,24 @@
 	bytes value;
 }
 
+enum EthTokenPermissions {
+	Mutable,
+	TokenOwner,
+	CollectionAdmin
+}
+
+/// @dev anonymous struct
+struct Tuple48 {
+	string field_0;
+	Tuple46[] field_1;
+}
+
+/// @dev anonymous struct
+struct Tuple46 {
+	EthTokenPermissions field_0;
+	bool field_1;
+}
+
 /// @title A contract that allows you to work with collections.
 /// @dev the ERC-165 identifier for this interface is 0xb5e1747f
 contract Collection is Dummy, ERC165 {
modifiedpallets/refungible/CHANGELOG.mddiffbeforeafterboth
--- a/pallets/refungible/CHANGELOG.md
+++ b/pallets/refungible/CHANGELOG.md
@@ -4,6 +4,16 @@
 
 <!-- bureaucrate goes here -->
 
+## [0.2.10] - 2022-12-16
+
+### Added
+
+- The function `tokenPropertyPermissions` and `setTokenPropertyPermissions` to `TokenProperties` interface.
+
+### Changed
+
+- Hide `setTokenPropertyPermission` function in `TokenProperties` interface.
+
 ## [0.2.9] - 2022-11-18
 
 ### Added
modifiedpallets/refungible/Cargo.tomldiffbeforeafterboth
--- a/pallets/refungible/Cargo.toml
+++ b/pallets/refungible/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "pallet-refungible"
-version = "0.2.8"
+version = "0.2.10"
 license = "GPLv3"
 edition = "2021"
 
modifiedpallets/refungible/src/erc.rsdiffbeforeafterboth
--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -33,7 +33,7 @@
 use pallet_common::{
 	CollectionHandle, CollectionPropertyPermissions, CommonCollectionOperations,
 	erc::{CommonEvmHandler, CollectionCall, static_property::key},
-	eth::EthCrossAccount,
+	eth::{EthCrossAccount, EthTokenPermissions},
 	Error as CommonError,
 };
 use pallet_evm::{account::CrossAccountId, PrecompileHandle};
@@ -63,6 +63,7 @@
 	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
 	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
 	#[weight(<SelfWeightOf<T>>::set_token_property_permissions(1))]
+	#[solidity(hide)]
 	fn set_token_property_permission(
 		&mut self,
 		caller: caller,
@@ -89,6 +90,77 @@
 		.map_err(dispatch_to_evm::<T>)
 	}
 
+	/// @notice Set permissions for token property.
+	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	/// @param permissions Permissions for keys.
+	#[weight(<SelfWeightOf<T>>::set_token_property_permissions(permissions.len() as u32))]
+	fn set_token_property_permissions(
+		&mut self,
+		caller: caller,
+		permissions: Vec<(string, Vec<(EthTokenPermissions, bool)>)>,
+	) -> Result<()> {
+		let caller = T::CrossAccountId::from_eth(caller);
+		const PERMISSIONS_FIELDS_COUNT: usize = 3;
+
+		let mut perms = Vec::new();
+
+		for (key, pp) in permissions {
+			if pp.len() > PERMISSIONS_FIELDS_COUNT {
+				return Err(alloc::format!(
+					"Actual number of fields {} for {}, which exceeds the maximum value of {}",
+					pp.len(),
+					stringify!(EthTokenPermissions),
+					PERMISSIONS_FIELDS_COUNT
+				)
+				.as_str()
+				.into());
+			}
+
+			let mut token_permission = PropertyPermission {
+				mutable: false,
+				collection_admin: false,
+				token_owner: false,
+			};
+
+			for (perm, value) in pp {
+				match perm {
+					EthTokenPermissions::Mutable => token_permission.mutable = value,
+					EthTokenPermissions::TokenOwner => token_permission.token_owner = value,
+					EthTokenPermissions::CollectionAdmin => {
+						token_permission.collection_admin = value
+					}
+				}
+			}
+
+			perms.push(PropertyKeyPermission {
+				key: key.into_bytes().try_into().map_err(|_| "too long key")?,
+				permission: token_permission,
+			});
+		}
+
+		<Pallet<T>>::set_token_property_permissions(self, &caller, perms)
+			.map_err(dispatch_to_evm::<T>)
+	}
+
+	/// @notice Get permissions for token properties.
+	fn token_property_permissions(
+		&self,
+	) -> Result<Vec<(string, Vec<(EthTokenPermissions, bool)>)>> {
+		let perms = <Pallet<T>>::token_property_permission(self.id);
+		Ok(perms
+			.into_iter()
+			.map(|(key, pp)| {
+				let key = string::from_utf8(key.into_inner()).expect("Stored key must be valid");
+				let pp = vec![
+					(EthTokenPermissions::Mutable, pp.mutable),
+					(EthTokenPermissions::TokenOwner, pp.token_owner),
+					(EthTokenPermissions::CollectionAdmin, pp.collection_admin),
+				];
+				(key, pp)
+			})
+			.collect())
+	}
+
 	/// @notice Set token property value.
 	/// @dev Throws error if `msg.sender` has no permission to edit the property.
 	/// @param tokenId ID of the token.
modifiedpallets/refungible/src/lib.rsdiffbeforeafterboth
--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -113,7 +113,7 @@
 	AccessMode, budget::Budget, CollectionId, CollectionFlags, CollectionPropertiesVec,
 	CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH,
 	MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,
-	PropertyScope, PropertyValue, TokenId, TrySetProperty,
+	PropertyScope, PropertyValue, TokenId, TrySetProperty, PropertiesPermissionMap,
 };
 
 pub use pallet::*;
@@ -1378,6 +1378,10 @@
 		<PalletCommon<T>>::set_token_property_permissions(collection, sender, property_permissions)
 	}
 
+	pub fn token_property_permission(collection_id: CollectionId) -> PropertiesPermissionMap {
+		<PalletCommon<T>>::property_permissions(collection_id)
+	}
+
 	pub fn set_scoped_token_property_permissions(
 		collection: &RefungibleHandle<T>,
 		sender: &T::CrossAccountId,
modifiedpallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/refungible/src/stubs/UniqueRefungible.soldiffbeforeafterboth
--- a/pallets/refungible/src/stubs/UniqueRefungible.sol
+++ b/pallets/refungible/src/stubs/UniqueRefungible.sol
@@ -18,30 +18,45 @@
 }
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0x91a97a68
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
 contract TokenProperties is Dummy, ERC165 {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) public {
+	// 	require(false, stub_error);
+	// 	key;
+	// 	isMutable;
+	// 	collectionAdmin;
+	// 	tokenOwner;
+	// 	dummy = 0;
+	// }
+
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param key Property key.
-	/// @param isMutable Permission to mutate property.
-	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	/// @dev EVM selector for this function is: 0x222d97fa,
-	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	function setTokenPropertyPermission(
-		string memory key,
-		bool isMutable,
-		bool collectionAdmin,
-		bool tokenOwner
-	) public {
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(Tuple53[] memory permissions) public {
 		require(false, stub_error);
-		key;
-		isMutable;
-		collectionAdmin;
-		tokenOwner;
+		permissions;
 		dummy = 0;
 	}
 
+	/// @notice Get permissions for token properties.
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() public view returns (Tuple53[] memory) {
+		require(false, stub_error);
+		dummy;
+		return new Tuple53[](0);
+	}
+
 	// /// @notice Set token property value.
 	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
 	// /// @param tokenId ID of the token.
@@ -118,6 +133,24 @@
 	bytes value;
 }
 
+enum EthTokenPermissions {
+	Mutable,
+	TokenOwner,
+	CollectionAdmin
+}
+
+/// @dev anonymous struct
+struct Tuple53 {
+	string field_0;
+	Tuple51[] field_1;
+}
+
+/// @dev anonymous struct
+struct Tuple51 {
+	EthTokenPermissions field_0;
+	bool field_1;
+}
+
 /// @title A contract that allows you to work with collections.
 /// @dev the ERC-165 identifier for this interface is 0xb5e1747f
 contract Collection is Dummy, ERC165 {
modifiedprimitives/data-structs/src/lib.rsdiffbeforeafterboth
--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -1010,7 +1010,7 @@
 pub type PropertyValue = BoundedBytes<ConstU32<MAX_PROPERTY_VALUE_LENGTH>>;
 
 /// Property permission.
-#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone)]
+#[derive(Encode, Decode, TypeInfo, Debug, MaxEncodedLen, PartialEq, Clone, Default)]
 #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
 pub struct PropertyPermission {
 	/// Permission to change the property and property permission.
modifiedtests/src/eth/abi/nonFungible.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/nonFungible.json
+++ b/tests/src/eth/abi/nonFungible.json
@@ -697,12 +697,29 @@
   },
   {
     "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" }
+      {
+        "components": [
+          { "internalType": "string", "name": "field_0", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum EthTokenPermissions",
+                "name": "field_0",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "field_1", "type": "bool" }
+            ],
+            "internalType": "struct Tuple46[]",
+            "name": "field_1",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct Tuple48[]",
+        "name": "permissions",
+        "type": "tuple[]"
+      }
     ],
-    "name": "setTokenPropertyPermission",
+    "name": "setTokenPropertyPermissions",
     "outputs": [],
     "stateMutability": "nonpayable",
     "type": "function"
@@ -743,6 +760,35 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "tokenPropertyPermissions",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "field_0", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum EthTokenPermissions",
+                "name": "field_0",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "field_1", "type": "bool" }
+            ],
+            "internalType": "struct Tuple46[]",
+            "name": "field_1",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct Tuple48[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
     "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
modifiedtests/src/eth/abi/reFungible.jsondiffbeforeafterboth
--- a/tests/src/eth/abi/reFungible.json
+++ b/tests/src/eth/abi/reFungible.json
@@ -679,12 +679,29 @@
   },
   {
     "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" }
+      {
+        "components": [
+          { "internalType": "string", "name": "field_0", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum EthTokenPermissions",
+                "name": "field_0",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "field_1", "type": "bool" }
+            ],
+            "internalType": "struct Tuple51[]",
+            "name": "field_1",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct Tuple53[]",
+        "name": "permissions",
+        "type": "tuple[]"
+      }
     ],
-    "name": "setTokenPropertyPermission",
+    "name": "setTokenPropertyPermissions",
     "outputs": [],
     "stateMutability": "nonpayable",
     "type": "function"
@@ -734,6 +751,35 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "tokenPropertyPermissions",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "string", "name": "field_0", "type": "string" },
+          {
+            "components": [
+              {
+                "internalType": "enum EthTokenPermissions",
+                "name": "field_0",
+                "type": "uint8"
+              },
+              { "internalType": "bool", "name": "field_1", "type": "bool" }
+            ],
+            "internalType": "struct Tuple51[]",
+            "name": "field_1",
+            "type": "tuple[]"
+          }
+        ],
+        "internalType": "struct Tuple53[]",
+        "name": "",
+        "type": "tuple[]"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
     "inputs": [
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -13,22 +13,28 @@
 }
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0x91a97a68
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
 interface TokenProperties is Dummy, ERC165 {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
+
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param key Property key.
-	/// @param isMutable Permission to mutate property.
-	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	/// @dev EVM selector for this function is: 0x222d97fa,
-	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	function setTokenPropertyPermission(
-		string memory key,
-		bool isMutable,
-		bool collectionAdmin,
-		bool tokenOwner
-	) external;
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(Tuple43[] memory permissions) external;
+
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() external view returns (Tuple43[] memory);
 
 	// /// @notice Set token property value.
 	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
@@ -79,6 +85,24 @@
 	bytes value;
 }
 
+enum EthTokenPermissions {
+	Mutable,
+	TokenOwner,
+	CollectionAdmin
+}
+
+/// @dev anonymous struct
+struct Tuple43 {
+	string field_0;
+	Tuple41[] field_1;
+}
+
+/// @dev anonymous struct
+struct Tuple41 {
+	EthTokenPermissions field_0;
+	bool field_1;
+}
+
 /// @title A contract that allows you to work with collections.
 /// @dev the ERC-165 identifier for this interface is 0xb5e1747f
 interface Collection is Dummy, ERC165 {
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -13,22 +13,29 @@
 }
 
 /// @title A contract that allows to set and delete token properties and change token property permissions.
-/// @dev the ERC-165 identifier for this interface is 0x91a97a68
+/// @dev the ERC-165 identifier for this interface is 0xde0695c2
 interface TokenProperties is Dummy, ERC165 {
+	// /// @notice Set permissions for token property.
+	// /// @dev Throws error if `msg.sender` is not admin or owner of the collection.
+	// /// @param key Property key.
+	// /// @param isMutable Permission to mutate property.
+	// /// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
+	// /// @param tokenOwner Permission to mutate property by token owner if property is mutable.
+	// /// @dev EVM selector for this function is: 0x222d97fa,
+	// ///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
+	// function setTokenPropertyPermission(string memory key, bool isMutable, bool collectionAdmin, bool tokenOwner) external;
+
 	/// @notice Set permissions for token property.
 	/// @dev Throws error if `msg.sender` is not admin or owner of the collection.
-	/// @param key Property key.
-	/// @param isMutable Permission to mutate property.
-	/// @param collectionAdmin Permission to mutate property by collection admin if property is mutable.
-	/// @param tokenOwner Permission to mutate property by token owner if property is mutable.
-	/// @dev EVM selector for this function is: 0x222d97fa,
-	///  or in textual repr: setTokenPropertyPermission(string,bool,bool,bool)
-	function setTokenPropertyPermission(
-		string memory key,
-		bool isMutable,
-		bool collectionAdmin,
-		bool tokenOwner
-	) external;
+	/// @param permissions Permissions for keys.
+	/// @dev EVM selector for this function is: 0xbd92983a,
+	///  or in textual repr: setTokenPropertyPermissions((string,(uint8,bool)[])[])
+	function setTokenPropertyPermissions(Tuple47[] memory permissions) external;
+
+	/// @notice Get permissions for token properties.
+	/// @dev EVM selector for this function is: 0xf23d7790,
+	///  or in textual repr: tokenPropertyPermissions()
+	function tokenPropertyPermissions() external view returns (Tuple47[] memory);
 
 	// /// @notice Set token property value.
 	// /// @dev Throws error if `msg.sender` has no permission to edit the property.
@@ -79,6 +86,24 @@
 	bytes value;
 }
 
+enum EthTokenPermissions {
+	Mutable,
+	TokenOwner,
+	CollectionAdmin
+}
+
+/// @dev anonymous struct
+struct Tuple47 {
+	string field_0;
+	Tuple45[] field_1;
+}
+
+/// @dev anonymous struct
+struct Tuple45 {
+	EthTokenPermissions field_0;
+	bool field_1;
+}
+
 /// @title A contract that allows you to work with collections.
 /// @dev the ERC-165 identifier for this interface is 0xb5e1747f
 interface Collection is Dummy, ERC165 {
modifiedtests/src/eth/events.test.tsdiffbeforeafterboth
--- a/tests/src/eth/events.test.ts
+++ b/tests/src/eth/events.test.ts
@@ -19,7 +19,7 @@
 import {EthUniqueHelper, itEth, usingEthPlaygrounds} from './util';
 import {IEvent, TCollectionMode} from '../util/playgrounds/types';
 import {Pallets, requirePalletsOrSkip} from '../util';
-import {NormalizedEvent} from './util/playgrounds/types';
+import {EthTokenPermissions, NormalizedEvent} from './util/playgrounds/types';
 
 let donor: IKeyringPair;
   
@@ -119,7 +119,13 @@
     ethEvents.push(event);
   });
   const {unsubscribe, collectedEvents: subEvents} = await helper.subscribeEvents([{section: 'common', names: ['PropertyPermissionSet']}]);
-  await collection.methods.setTokenPropertyPermission('testKey', true, true, true).send({from: owner});
+  await collection.methods.setTokenPropertyPermissions([
+    ['A', [
+      [EthTokenPermissions.Mutable, true], 
+      [EthTokenPermissions.TokenOwner, true], 
+      [EthTokenPermissions.CollectionAdmin, true]],
+    ],
+  ]).send({from: owner});
   await helper.wait.newBlocks(1);
   expect(ethEvents).to.be.like([
     {
@@ -374,7 +380,13 @@
   const collectionHelper = helper.ethNativeContract.collectionHelpers(owner);
   const result = await collection.methods.mint(owner).send({from: owner});
   const tokenId = result.events.Transfer.returnValues.tokenId;
-  await collection.methods.setTokenPropertyPermission('A', true, true, true).send({from: owner});
+  await collection.methods.setTokenPropertyPermissions([
+    ['A', [
+      [EthTokenPermissions.Mutable, true], 
+      [EthTokenPermissions.TokenOwner, true], 
+      [EthTokenPermissions.CollectionAdmin, true]],
+    ],
+  ]).send({from: owner});
 
 
   const ethEvents: any = [];
modifiedtests/src/eth/tokenProperties.test.tsdiffbeforeafterboth
--- a/tests/src/eth/tokenProperties.test.ts
+++ b/tests/src/eth/tokenProperties.test.ts
@@ -20,6 +20,7 @@
 import {ITokenPropertyPermission} from '../util/playgrounds/types';
 import {Pallets} from '../util';
 import {UniqueNFTCollection, UniqueNFToken, UniqueRFTCollection} from '../util/playgrounds/unique';
+import {EthTokenPermissions} from './util/playgrounds/types';
 
 describe('EVM token properties', () => {
   let donor: IKeyringPair;
@@ -32,25 +33,170 @@
     });
   });
 
-  itEth('Can be reconfigured', async({helper}) => {
-    const caller = await helper.eth.createAccountWithBalance(donor);
-    for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
-      const collection = await helper.nft.mintCollection(alice);
-      await collection.addAdmin(alice, {Ethereum: caller});
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Set and get token property permissions`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
+        const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+        const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+        await collection.methods.addCollectionAdminCross(caller).send({from: owner});
+
+        await collection.methods.setTokenPropertyPermissions([
+          ['testKey', [
+            [EthTokenPermissions.Mutable, mutable], 
+            [EthTokenPermissions.TokenOwner, tokenOwner], 
+            [EthTokenPermissions.CollectionAdmin, collectionAdmin]],
+          ],
+        ]).send({from: caller.eth});
       
-      const address = helper.ethAddress.fromCollectionId(collection.collectionId);
-      const contract = helper.ethNativeContract.collection(address, 'nft', caller);
-  
-      await contract.methods.setTokenPropertyPermission('testKey', mutable, collectionAdmin, tokenOwner).send({from: caller});
-  
-      expect(await collection.getPropertyPermissions()).to.be.deep.equal([{
-        key: 'testKey',
-        permission: {mutable, collectionAdmin, tokenOwner},
-      }]);
-    }
-  });
+        expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([{
+          key: 'testKey',
+          permission: {mutable, collectionAdmin, tokenOwner},
+        }]);
 
+        expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
+          ['testKey', [
+            [EthTokenPermissions.Mutable.toString(), mutable], 
+            [EthTokenPermissions.TokenOwner.toString(), tokenOwner], 
+            [EthTokenPermissions.CollectionAdmin.toString(), collectionAdmin]],
+          ],
+        ]);
+      }
+    }));
+
   [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as owner`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+        ['testKey_1', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, false], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+        ['testKey_2', [
+          [EthTokenPermissions.Mutable, false], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, false]],
+        ],
+      ]).send({from: owner});
+      
+      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
+        {
+          key: 'testKey_0',
+          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_1',
+          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_2',
+          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
+        },
+      ]);
+
+      expect(await collection.methods.tokenPropertyPermissions().call({from: owner})).to.be.like([
+        ['testKey_0', [
+          [EthTokenPermissions.Mutable.toString(), true], 
+          [EthTokenPermissions.TokenOwner.toString(), true], 
+          [EthTokenPermissions.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_1', [
+          [EthTokenPermissions.Mutable.toString(), true], 
+          [EthTokenPermissions.TokenOwner.toString(), false], 
+          [EthTokenPermissions.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_2', [
+          [EthTokenPermissions.Mutable.toString(), false], 
+          [EthTokenPermissions.TokenOwner.toString(), true], 
+          [EthTokenPermissions.CollectionAdmin.toString(), false]],
+        ],
+      ]);
+      
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Set and get multiple token property permissions as admin`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.ethCrossAccount.createAccountWithBalance(donor);
+      
+      const {collectionId, collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+      await collection.methods.addCollectionAdminCross(caller).send({from: owner});
+
+      await collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+        ['testKey_1', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, false], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+        ['testKey_2', [
+          [EthTokenPermissions.Mutable, false], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, false]],
+        ],
+      ]).send({from: caller.eth});
+      
+      expect(await helper[testCase.mode].getPropertyPermissions(collectionId)).to.be.deep.equal([
+        {
+          key: 'testKey_0',
+          permission: {mutable: true, tokenOwner: true, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_1',
+          permission: {mutable: true, tokenOwner: false, collectionAdmin: true},
+        },
+        {
+          key: 'testKey_2',
+          permission: {mutable: false, tokenOwner: true, collectionAdmin: false},
+        },
+      ]);
+
+      expect(await collection.methods.tokenPropertyPermissions().call({from: caller.eth})).to.be.like([
+        ['testKey_0', [
+          [EthTokenPermissions.Mutable.toString(), true], 
+          [EthTokenPermissions.TokenOwner.toString(), true], 
+          [EthTokenPermissions.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_1', [
+          [EthTokenPermissions.Mutable.toString(), true], 
+          [EthTokenPermissions.TokenOwner.toString(), false], 
+          [EthTokenPermissions.CollectionAdmin.toString(), true]],
+        ],
+        ['testKey_2', [
+          [EthTokenPermissions.Mutable.toString(), false], 
+          [EthTokenPermissions.TokenOwner.toString(), true], 
+          [EthTokenPermissions.CollectionAdmin.toString(), false]],
+        ],
+      ]);
+      
+    }));
+
+  [
     {
       method: 'setProperties',
       methodParams: [[{key: 'testKey1', value: Buffer.from('testValue1')}, {key: 'testKey2', value: Buffer.from('testValue2')}]],
@@ -301,6 +447,47 @@
       const actualProps = await collectionEvm.methods.properties(token.tokenId, []).call();
       expect(actualProps).to.deep.eq(expectedProps);
     }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions as non owner or admin`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+      const caller = await helper.eth.createAccountWithBalance(donor);
+        
+      const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+  
+      await expect(collection.methods.setTokenPropertyPermissions([
+        ['testKey_0', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+      ]).call({from: caller})).to.be.rejectedWith('NoPermission');  
+    }));
+
+  [
+    {mode: 'nft' as const, requiredPallets: []},
+    {mode: 'rft' as const, requiredPallets: [Pallets.ReFungible]},
+  ].map(testCase =>
+    itEth.ifWithPallets(`[${testCase.mode}] Cant set token property permissions with invalid character`, testCase.requiredPallets, async({helper}) => {
+      const owner = await helper.eth.createAccountWithBalance(donor);
+        
+      const {collectionAddress} = await helper.eth.createCollection(testCase.mode, owner, 'A', 'B', 'C');
+      const collection = await helper.ethNativeContract.collection(collectionAddress, testCase.mode, owner);
+  
+      await expect(collection.methods.setTokenPropertyPermissions([
+        // "Space" is invalid character
+        ['testKey 0', [
+          [EthTokenPermissions.Mutable, true], 
+          [EthTokenPermissions.TokenOwner, true], 
+          [EthTokenPermissions.CollectionAdmin, true]],
+        ],
+      ]).call({from: owner})).to.be.rejectedWith('InvalidCharacterInPropertyKey');  
+    }));
+  
 });
 
 
modifiedtests/src/eth/util/playgrounds/types.tsdiffbeforeafterboth
--- a/tests/src/eth/util/playgrounds/types.ts
+++ b/tests/src/eth/util/playgrounds/types.ts
@@ -20,3 +20,8 @@
 
 export type EthProperty = string[];
 
+export enum EthTokenPermissions {
+  Mutable,
+  TokenOwner,
+  CollectionAdmin
+}
\ No newline at end of file