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

difftreelog

Merge pull request #574 from UniqueNetwork/feature/eth_passthrought

Yaroslav Bolyukin2022-09-14parents: #a711701 #d8d2ace.patch.diff
in: master
Feature/eth passthrought

19 files changed

modifiedpallets/common/src/erc.rsdiffbeforeafterboth
20 solidity_interface, solidity, ToLog,20 solidity_interface, solidity, ToLog,
21 types::*,21 types::*,
22 execution::{Result, Error},22 execution::{Result, Error},
23 weight,
23};24};
24pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};25pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};
25use pallet_evm_coder_substrate::dispatch_to_evm;26use pallet_evm_coder_substrate::dispatch_to_evm;
31use alloc::format;32use alloc::format;
3233
33use crate::{34use crate::{
34 Pallet, CollectionHandle, Config, CollectionProperties,35 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,
35 eth::{convert_cross_account_to_uint256, convert_uint256_to_cross_account},36 eth::{
37 convert_cross_account_to_uint256, convert_uint256_to_cross_account,
38 convert_cross_account_to_tuple,
39 },
40 weights::WeightInfo,
36};41};
3742
38/// Events for ethereum collection helper.43/// Events for ethereum collection helper.
69 ///74 ///
70 /// @param key Property key.75 /// @param key Property key.
71 /// @param value Propery value.76 /// @param value Propery value.
77 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]
72 fn set_collection_property(78 fn set_collection_property(
73 &mut self,79 &mut self,
74 caller: caller,80 caller: caller,
88 /// Delete collection property.94 /// Delete collection property.
89 ///95 ///
90 /// @param key Property key.96 /// @param key Property key.
97 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]
91 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {98 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {
99 self.consume_store_reads_and_writes(1, 1)?;
100
92 let caller = T::CrossAccountId::from_eth(caller);101 let caller = T::CrossAccountId::from_eth(caller);
93 let key = <Vec<u8>>::from(key)102 let key = <Vec<u8>>::from(key)
94 .try_into()103 .try_into()
120 ///129 ///
121 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.130 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.
122 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {131 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {
132 self.consume_store_reads_and_writes(1, 1)?;
133
123 check_is_owner_or_admin(caller, self)?;134 check_is_owner_or_admin(caller, self)?;
124135
125 let sponsor = T::CrossAccountId::from_eth(sponsor);136 let sponsor = T::CrossAccountId::from_eth(sponsor);
138 caller: caller,149 caller: caller,
139 sponsor: uint256,150 sponsor: uint256,
140 ) -> Result<void> {151 ) -> Result<void> {
152 self.consume_store_reads_and_writes(1, 1)?;
153
141 check_is_owner_or_admin(caller, self)?;154 check_is_owner_or_admin(caller, self)?;
142155
143 let sponsor = convert_uint256_to_cross_account::<T>(sponsor);156 let sponsor = convert_uint256_to_cross_account::<T>(sponsor);
146 save(self)159 save(self)
147 }160 }
148161
149 // /// Whether there is a pending sponsor.162 /// Whether there is a pending sponsor.
150 fn has_collection_pending_sponsor(&self) -> Result<bool> {163 fn has_collection_pending_sponsor(&self) -> Result<bool> {
151 Ok(matches!(164 Ok(matches!(
152 self.collection.sponsorship,165 self.collection.sponsorship,
158 ///171 ///
159 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.172 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.
160 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {173 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {
174 self.consume_store_writes(1)?;
175
161 let caller = T::CrossAccountId::from_eth(caller);176 let caller = T::CrossAccountId::from_eth(caller);
162 if !self177 if !self
163 .confirm_sponsorship(caller.as_sub())178 .confirm_sponsorship(caller.as_sub())
170185
171 /// Remove collection sponsor.186 /// Remove collection sponsor.
172 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {187 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {
188 self.consume_store_reads_and_writes(1, 1)?;
173 check_is_owner_or_admin(caller, self)?;189 check_is_owner_or_admin(caller, self)?;
174 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;190 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;
175 save(self)191 save(self)
206 /// @param value Value of the limit.222 /// @param value Value of the limit.
207 #[solidity(rename_selector = "setCollectionLimit")]223 #[solidity(rename_selector = "setCollectionLimit")]
208 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {224 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {
225 self.consume_store_reads_and_writes(1, 1)?;
226
209 check_is_owner_or_admin(caller, self)?;227 check_is_owner_or_admin(caller, self)?;
210 let mut limits = self.limits.clone();228 let mut limits = self.limits.clone();
211229
249 /// @param value Value of the limit.267 /// @param value Value of the limit.
250 #[solidity(rename_selector = "setCollectionLimit")]268 #[solidity(rename_selector = "setCollectionLimit")]
251 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {269 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {
270 self.consume_store_reads_and_writes(1, 1)?;
271
252 check_is_owner_or_admin(caller, self)?;272 check_is_owner_or_admin(caller, self)?;
253 let mut limits = self.limits.clone();273 let mut limits = self.limits.clone();
254274
275 }295 }
276296
277 /// Get contract address.297 /// Get contract address.
278 fn contract_address(&self, _caller: caller) -> Result<address> {298 fn contract_address(&self) -> Result<address> {
279 Ok(crate::eth::collection_id_to_address(self.id))299 Ok(crate::eth::collection_id_to_address(self.id))
280 }300 }
281301
286 caller: caller,306 caller: caller,
287 new_admin: uint256,307 new_admin: uint256,
288 ) -> Result<void> {308 ) -> Result<void> {
309 self.consume_store_writes(2)?;
310
289 let caller = T::CrossAccountId::from_eth(caller);311 let caller = T::CrossAccountId::from_eth(caller);
290 let new_admin = convert_uint256_to_cross_account::<T>(new_admin);312 let new_admin = convert_uint256_to_cross_account::<T>(new_admin);
291 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;313 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
299 caller: caller,321 caller: caller,
300 admin: uint256,322 admin: uint256,
301 ) -> Result<void> {323 ) -> Result<void> {
324 self.consume_store_writes(2)?;
325
302 let caller = T::CrossAccountId::from_eth(caller);326 let caller = T::CrossAccountId::from_eth(caller);
303 let admin = convert_uint256_to_cross_account::<T>(admin);327 let admin = convert_uint256_to_cross_account::<T>(admin);
304 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;328 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
308 /// Add collection admin.332 /// Add collection admin.
309 /// @param newAdmin Address of the added administrator.333 /// @param newAdmin Address of the added administrator.
310 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {334 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {
335 self.consume_store_writes(2)?;
336
311 let caller = T::CrossAccountId::from_eth(caller);337 let caller = T::CrossAccountId::from_eth(caller);
312 let new_admin = T::CrossAccountId::from_eth(new_admin);338 let new_admin = T::CrossAccountId::from_eth(new_admin);
313 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;339 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;
318 ///344 ///
319 /// @param admin Address of the removed administrator.345 /// @param admin Address of the removed administrator.
320 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {346 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {
347 self.consume_store_writes(2)?;
348
321 let caller = T::CrossAccountId::from_eth(caller);349 let caller = T::CrossAccountId::from_eth(caller);
322 let admin = T::CrossAccountId::from_eth(admin);350 let admin = T::CrossAccountId::from_eth(admin);
323 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;351 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;
329 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'357 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'
330 #[solidity(rename_selector = "setCollectionNesting")]358 #[solidity(rename_selector = "setCollectionNesting")]
331 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {359 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {
360 self.consume_store_reads_and_writes(1, 1)?;
361
332 check_is_owner_or_admin(caller, self)?;362 check_is_owner_or_admin(caller, self)?;
333363
334 let mut permissions = self.collection.permissions.clone();364 let mut permissions = self.collection.permissions.clone();
358 enable: bool,388 enable: bool,
359 collections: Vec<address>,389 collections: Vec<address>,
360 ) -> Result<void> {390 ) -> Result<void> {
391 self.consume_store_reads_and_writes(1, 1)?;
392
361 if collections.is_empty() {393 if collections.is_empty() {
362 return Err("no addresses provided".into());394 return Err("no addresses provided".into());
363 }395 }
401 /// 0 for Normal433 /// 0 for Normal
402 /// 1 for AllowList434 /// 1 for AllowList
403 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {435 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {
436 self.consume_store_reads_and_writes(1, 1)?;
437
404 check_is_owner_or_admin(caller, self)?;438 check_is_owner_or_admin(caller, self)?;
405 let permissions = CollectionPermissions {439 let permissions = CollectionPermissions {
406 access: Some(match mode {440 access: Some(match mode {
420 save(self)454 save(self)
421 }455 }
422456
457 /// Checks that user allowed to operate with collection.
458 ///
459 /// @param user User address to check.
460 fn allowed(&self, user: address) -> Result<bool> {
461 Ok(Pallet::<T>::allowed(
462 self.id,
463 T::CrossAccountId::from_eth(user),
464 ))
465 }
466
423 /// Add the user to the allowed list.467 /// Add the user to the allowed list.
424 ///468 ///
425 /// @param user Address of a trusted user.469 /// @param user Address of a trusted user.
426 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {470 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {
471 self.consume_store_writes(1)?;
472
427 let caller = T::CrossAccountId::from_eth(caller);473 let caller = T::CrossAccountId::from_eth(caller);
428 let user = T::CrossAccountId::from_eth(user);474 let user = T::CrossAccountId::from_eth(user);
429 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;475 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
430 Ok(())476 Ok(())
431 }477 }
432478
479 /// Add substrate user to allowed list.
480 ///
481 /// @param user User substrate address.
482 fn add_to_collection_allow_list_substrate(
483 &mut self,
484 caller: caller,
485 user: uint256,
486 ) -> Result<void> {
487 self.consume_store_writes(1)?;
488
489 let caller = T::CrossAccountId::from_eth(caller);
490 let user = convert_uint256_to_cross_account::<T>(user);
491 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;
492 Ok(())
493 }
494
433 /// Remove the user from the allowed list.495 /// Remove the user from the allowed list.
434 ///496 ///
435 /// @param user Address of a removed user.497 /// @param user Address of a removed user.
436 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {498 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {
499 self.consume_store_writes(1)?;
500
437 let caller = T::CrossAccountId::from_eth(caller);501 let caller = T::CrossAccountId::from_eth(caller);
438 let user = T::CrossAccountId::from_eth(user);502 let user = T::CrossAccountId::from_eth(user);
439 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;503 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
440 Ok(())504 Ok(())
441 }505 }
442506
507 /// Remove substrate user from allowed list.
508 ///
509 /// @param user User substrate address.
510 fn remove_from_collection_allow_list_substrate(
511 &mut self,
512 caller: caller,
513 user: uint256,
514 ) -> Result<void> {
515 self.consume_store_writes(1)?;
516
517 let caller = T::CrossAccountId::from_eth(caller);
518 let user = convert_uint256_to_cross_account::<T>(user);
519 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;
520 Ok(())
521 }
522
443 /// Switch permission for minting.523 /// Switch permission for minting.
444 ///524 ///
445 /// @param mode Enable if "true".525 /// @param mode Enable if "true".
446 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {526 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {
527 self.consume_store_reads_and_writes(1, 1)?;
528
447 check_is_owner_or_admin(caller, self)?;529 check_is_owner_or_admin(caller, self)?;
448 let permissions = CollectionPermissions {530 let permissions = CollectionPermissions {
449 mint_mode: Some(mode),531 mint_mode: Some(mode),
481 /// Returns collection type563 /// Returns collection type
482 ///564 ///
483 /// @return `Fungible` or `NFT` or `ReFungible`565 /// @return `Fungible` or `NFT` or `ReFungible`
484 fn unique_collection_type(&mut self) -> Result<string> {566 fn unique_collection_type(&self) -> Result<string> {
485 let mode = match self.collection.mode {567 let mode = match self.collection.mode {
486 CollectionMode::Fungible(_) => "Fungible",568 CollectionMode::Fungible(_) => "Fungible",
487 CollectionMode::NFT => "NFT",569 CollectionMode::NFT => "NFT",
490 Ok(mode.into())572 Ok(mode.into())
491 }573 }
492574
575 /// Get collection owner.
576 ///
577 /// @return Tuble with sponsor address and his substrate mirror.
578 /// If address is canonical then substrate mirror is zero and vice versa.
579 fn collection_owner(&self) -> Result<(address, uint256)> {
580 Ok(convert_cross_account_to_tuple::<T>(
581 &T::CrossAccountId::from_sub(self.owner.clone()),
582 ))
583 }
584
493 /// Changes collection owner to another account585 /// Changes collection owner to another account
494 ///586 ///
495 /// @dev Owner can be changed only by current owner587 /// @dev Owner can be changed only by current owner
496 /// @param newOwner new owner account588 /// @param newOwner new owner account
497 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {589 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {
590 self.consume_store_writes(1)?;
591
498 let caller = T::CrossAccountId::from_eth(caller);592 let caller = T::CrossAccountId::from_eth(caller);
499 let new_owner = T::CrossAccountId::from_eth(new_owner);593 let new_owner = T::CrossAccountId::from_eth(new_owner);
500 self.set_owner_internal(caller, new_owner)594 self.set_owner_internal(caller, new_owner)
506 /// @dev Owner can be changed only by current owner600 /// @dev Owner can be changed only by current owner
507 /// @param newOwner new owner substrate account601 /// @param newOwner new owner substrate account
508 fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {602 fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {
603 self.consume_store_writes(1)?;
604
509 let caller = T::CrossAccountId::from_eth(caller);605 let caller = T::CrossAccountId::from_eth(caller);
510 let new_owner = convert_uint256_to_cross_account::<T>(new_owner);606 let new_owner = convert_uint256_to_cross_account::<T>(new_owner);
511 self.set_owner_internal(caller, new_owner)607 self.set_owner_internal(caller, new_owner)
512 .map_err(dispatch_to_evm::<T>)608 .map_err(dispatch_to_evm::<T>)
513 }609 }
610
611 // TODO: need implement AbiWriter for &Vec<T>
612 // fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {
613 // let result = pallet_common::IsAdmin::<T>::iter_prefix((self.id,))
614 // .map(|(admin, _)| pallet_common::eth::convert_cross_account_to_tuple::<T>(&admin))
615 // .collect();
616 // Ok(result)
617 // }
514}618}
515619
620/// ### Note
621/// Do not forget to add: `self.consume_store_reads(1)?;`
516fn check_is_owner_or_admin<T: Config>(622fn check_is_owner_or_admin<T: Config>(
517 caller: caller,623 caller: caller,
518 collection: &CollectionHandle<T>,624 collection: &CollectionHandle<T>,
524 Ok(caller)630 Ok(caller)
525}631}
526632
633/// ### Note
634/// Do not forget to add: `self.consume_store_writes(1)?;`
527fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {635fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {
528 // TODO possibly delete for the lack of transaction
529 collection.consume_store_writes(1)?;
530 collection636 collection
531 .check_is_internal()637 .check_is_internal()
532 .map_err(dispatch_to_evm::<T>)?;638 .map_err(dispatch_to_evm::<T>)?;
modifiedpallets/common/src/eth.rsdiffbeforeafterboth
--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -16,7 +16,7 @@
 
 //! The module contains a number of functions for converting and checking ethereum identifiers.
 
-use evm_coder::types::uint256;
+use evm_coder::types::{uint256, address};
 pub use pallet_evm::account::{Config, CrossAccountId};
 use sp_core::H160;
 use up_data_structs::CollectionId;
@@ -69,3 +69,19 @@
 	let account_id = T::AccountId::from(new_admin_arr);
 	T::CrossAccountId::from_sub(account_id)
 }
+
+/// Convert `CrossAccountId` to `(address, uint256)`.
+pub fn convert_cross_account_to_tuple<T: Config>(
+	cross_account_id: &T::CrossAccountId,
+) -> (address, uint256)
+where
+	T::AccountId: AsRef<[u8; 32]>,
+{
+	if cross_account_id.is_canonical_substrate() {
+		let sub = convert_cross_account_to_uint256::<T>(cross_account_id);
+		(Default::default(), sub)
+	} else {
+		let eth = *cross_account_id.as_eth();
+		(eth, Default::default())
+	}
+}
modifiedpallets/common/src/lib.rsdiffbeforeafterboth
--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -112,7 +112,6 @@
 	RmrkBoundedTheme,
 	RmrkNftChild,
 	CollectionPermissions,
-	SchemaVersion,
 };
 
 pub use pallet::*;
@@ -202,6 +201,21 @@
 			))
 	}
 
+	/// Consume gas for reading and writing.
+	pub fn consume_store_reads_and_writes(
+		&self,
+		reads: u64,
+		writes: u64,
+	) -> evm_coder::execution::Result<()> {
+		let weight = <T as frame_system::Config>::DbWeight::get();
+		let reads = weight.read.saturating_mul(reads);
+		let writes = weight.read.saturating_mul(writes);
+		self.recorder
+			.consume_gas(T::GasWeightMapping::weight_to_gas(
+				reads.saturating_add(writes),
+			))
+	}
+
 	/// Save collection to storage.
 	pub fn save(&self) -> DispatchResult {
 		<CollectionById<T>>::insert(self.id, &self.collection);
@@ -310,6 +324,8 @@
 	}
 
 	/// Changes collection owner to another account
+	/// #### Store read/writes
+	/// 1 writes
 	fn set_owner_internal(
 		&mut self,
 		caller: T::CrossAccountId,
@@ -1292,6 +1308,8 @@
 	}
 
 	/// Toggle `user` participation in the `collection`'s allow list.
+	/// #### Store read/writes
+	/// 1 writes
 	pub fn toggle_allowlist(
 		collection: &CollectionHandle<T>,
 		sender: &T::CrossAccountId,
@@ -1312,6 +1330,8 @@
 	}
 
 	/// Toggle `user` participation in the `collection`'s admin list.
+	/// #### Store read/writes
+	/// 2 writes
 	pub fn toggle_admin(
 		collection: &CollectionHandle<T>,
 		sender: &T::CrossAccountId,
modifiedpallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth
--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -172,14 +172,9 @@
 	fn get_sponsor(&self, contract_address: address) -> Result<(address, uint256)> {
 		let sponsor =
 			Pallet::<T>::get_sponsor(contract_address).ok_or("Contract has no sponsor")?;
-		let result: (address, uint256) = if sponsor.is_canonical_substrate() {
-			let sponsor = pallet_common::eth::convert_cross_account_to_uint256::<T>(&sponsor);
-			(Default::default(), sponsor)
-		} else {
-			let sponsor = *sponsor.as_eth();
-			(sponsor, Default::default())
-		};
-		Ok(result)
+		Ok(pallet_common::eth::convert_cross_account_to_tuple::<T>(
+			&sponsor,
+		))
 	}
 
 	/// Check tat contract has confirmed sponsor.
modifiedpallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth
--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -22,7 +22,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -255,6 +255,18 @@
 		dummy = 0;
 	}
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) public view returns (bool) {
+		require(false, stub_error);
+		user;
+		dummy;
+		return false;
+	}
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -266,6 +278,17 @@
 		dummy = 0;
 	}
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -277,6 +300,17 @@
 		dummy = 0;
 	}
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -325,6 +359,18 @@
 		return "";
 	}
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() public view returns (Tuple6 memory) {
+		require(false, stub_error);
+		dummy;
+		return Tuple6(0x0000000000000000000000000000000000000000, 0);
+	}
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
modifiedpallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterboth

binary blob — no preview

modifiedpallets/nonfungible/src/stubs/UniqueNFT.soldiffbeforeafterboth
--- a/pallets/nonfungible/src/stubs/UniqueNFT.sol
+++ b/pallets/nonfungible/src/stubs/UniqueNFT.sol
@@ -99,7 +99,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -332,6 +332,18 @@
 		dummy = 0;
 	}
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) public view returns (bool) {
+		require(false, stub_error);
+		user;
+		dummy;
+		return false;
+	}
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -343,6 +355,17 @@
 		dummy = 0;
 	}
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -354,6 +377,17 @@
 		dummy = 0;
 	}
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -402,6 +436,18 @@
 		return "";
 	}
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() public view returns (Tuple17 memory) {
+		require(false, stub_error);
+		dummy;
+		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+	}
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
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
@@ -99,7 +99,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 contract Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -332,6 +332,18 @@
 		dummy = 0;
 	}
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) public view returns (bool) {
+		require(false, stub_error);
+		user;
+		dummy;
+		return false;
+	}
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -343,6 +355,17 @@
 		dummy = 0;
 	}
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -354,6 +377,17 @@
 		dummy = 0;
 	}
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) public {
+		require(false, stub_error);
+		user;
+		dummy = 0;
+	}
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -402,6 +436,18 @@
 		return "";
 	}
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() public view returns (Tuple17 memory) {
+		require(false, stub_error);
+		dummy;
+		return Tuple17(0x0000000000000000000000000000000000000000, 0);
+	}
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
modifiedtests/src/eth/allowlist.test.tsdiffbeforeafterboth
--- a/tests/src/eth/allowlist.test.ts
+++ b/tests/src/eth/allowlist.test.ts
@@ -14,10 +14,22 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+import {IKeyringPair} from '@polkadot/types/types';
 import {expect} from 'chai';
-import {contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3} from './util/helpers';
+import {isAllowlisted, normalizeAccountId} from '../util/helpers';
+import {
+  contractHelpers,
+  createEthAccount,
+  createEthAccountWithBalance,
+  deployFlipper,
+  evmCollection,
+  evmCollectionHelpers,
+  getCollectionAddressFromResult,
+  itWeb3,
+} from './util/helpers';
+import {itEth, usingEthPlaygrounds} from './util/playgrounds';
 
-describe('EVM allowlist', () => {
+describe('EVM contract allowlist', () => {
   itWeb3('Contract allowlist can be toggled', async ({api, web3, privateKeyWrapper}) => {
     const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
     const flipper = await deployFlipper(web3, owner);
@@ -58,3 +70,79 @@
     expect(await flipper.methods.getValue().call()).to.be.false;
   });
 });
+
+describe('EVM collection allowlist', () => {
+  let donor: IKeyringPair;
+
+  before(async function() {
+    await usingEthPlaygrounds(async (_helper, privateKey) => {
+      donor = privateKey('//Alice');
+    });
+  });
+  
+  itEth('Collection allowlist can be added and removed by [eth] address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const user = helper.eth.createAccount();
+    
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
+    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
+    
+    await collectionEvm.methods.removeFromCollectionAllowList(user).send({from: owner});
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
+  });
+
+  itEth('Collection allowlist can be added and removed by [sub] address', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const user = donor;
+    
+    const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+    await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+    
+    await collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+  });
+
+  itEth('Collection allowlist can not be add and remove [eth] address by not owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const user = helper.eth.createAccount();
+    
+    const {collectionAddress} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
+    await expect(collectionEvm.methods.addToCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.false;
+    await collectionEvm.methods.addToCollectionAllowList(user).send({from: owner});
+    
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
+    await expect(collectionEvm.methods.removeFromCollectionAllowList(user).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await collectionEvm.methods.allowed(user).call({from: owner})).to.be.true;
+  });
+
+  itEth('Collection allowlist can not be add and remove [sub] address by not owner', async ({helper}) => {
+    const owner = await helper.eth.createAccountWithBalance(donor);
+    const notOwner = await helper.eth.createAccountWithBalance(donor);
+    const user = donor;
+    
+    const {collectionAddress, collectionId} = await helper.eth.createNonfungibleCollection(owner, 'A', 'B', 'C');
+    const collectionEvm = helper.ethNativeContract.collection(collectionAddress, 'nft', owner);
+    
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+    await expect(collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.false;
+    await collectionEvm.methods.addToCollectionAllowListSubstrate(user.addressRaw).send({from: owner});
+    
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+    await expect(collectionEvm.methods.removeFromCollectionAllowListSubstrate(user.addressRaw).call({from: notOwner})).to.be.rejectedWith('NoPermission');
+    expect(await helper.collection.allowed(collectionId, {Substrate: user.address})).to.be.true;
+  });
+});
modifiedtests/src/eth/api/UniqueFungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -13,7 +13,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -164,6 +164,13 @@
 	///  or in textual repr: setCollectionAccess(uint8)
 	function setCollectionAccess(uint8 mode) external;
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) external view returns (bool);
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -171,6 +178,13 @@
 	///  or in textual repr: addToCollectionAllowList(address)
 	function addToCollectionAllowList(address user) external;
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -178,6 +192,13 @@
 	///  or in textual repr: removeFromCollectionAllowList(address)
 	function removeFromCollectionAllowList(address user) external;
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -208,6 +229,14 @@
 	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (Tuple6 memory);
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
modifiedtests/src/eth/api/UniqueNFT.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueNFT.sol
+++ b/tests/src/eth/api/UniqueNFT.sol
@@ -65,7 +65,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -216,6 +216,13 @@
 	///  or in textual repr: setCollectionAccess(uint8)
 	function setCollectionAccess(uint8 mode) external;
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) external view returns (bool);
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -223,6 +230,13 @@
 	///  or in textual repr: addToCollectionAllowList(address)
 	function addToCollectionAllowList(address user) external;
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -230,6 +244,13 @@
 	///  or in textual repr: removeFromCollectionAllowList(address)
 	function removeFromCollectionAllowList(address user) external;
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -260,6 +281,14 @@
 	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (Tuple17 memory);
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
modifiedtests/src/eth/api/UniqueRefungible.soldiffbeforeafterboth
--- a/tests/src/eth/api/UniqueRefungible.sol
+++ b/tests/src/eth/api/UniqueRefungible.sol
@@ -65,7 +65,7 @@
 }
 
 /// @title A contract that allows you to work with collections.
-/// @dev the ERC-165 identifier for this interface is 0xe54be640
+/// @dev the ERC-165 identifier for this interface is 0x9f70d4e0
 interface Collection is Dummy, ERC165 {
 	/// Set collection property.
 	///
@@ -216,6 +216,13 @@
 	///  or in textual repr: setCollectionAccess(uint8)
 	function setCollectionAccess(uint8 mode) external;
 
+	/// Checks that user allowed to operate with collection.
+	///
+	/// @param user User address to check.
+	/// @dev EVM selector for this function is: 0xd63a8e11,
+	///  or in textual repr: allowed(address)
+	function allowed(address user) external view returns (bool);
+
 	/// Add the user to the allowed list.
 	///
 	/// @param user Address of a trusted user.
@@ -223,6 +230,13 @@
 	///  or in textual repr: addToCollectionAllowList(address)
 	function addToCollectionAllowList(address user) external;
 
+	/// Add substrate user to allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xd06ad267,
+	///  or in textual repr: addToCollectionAllowListSubstrate(uint256)
+	function addToCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Remove the user from the allowed list.
 	///
 	/// @param user Address of a removed user.
@@ -230,6 +244,13 @@
 	///  or in textual repr: removeFromCollectionAllowList(address)
 	function removeFromCollectionAllowList(address user) external;
 
+	/// Remove substrate user from allowed list.
+	///
+	/// @param user User substrate address.
+	/// @dev EVM selector for this function is: 0xa31913ed,
+	///  or in textual repr: removeFromCollectionAllowListSubstrate(uint256)
+	function removeFromCollectionAllowListSubstrate(uint256 user) external;
+
 	/// Switch permission for minting.
 	///
 	/// @param mode Enable if "true".
@@ -260,6 +281,14 @@
 	///  or in textual repr: uniqueCollectionType()
 	function uniqueCollectionType() external returns (string memory);
 
+	/// Get collection owner.
+	///
+	/// @return Tuble with sponsor address and his substrate mirror.
+	/// If address is canonical then substrate mirror is zero and vice versa.
+	/// @dev EVM selector for this function is: 0xdf727d3b,
+	///  or in textual repr: collectionOwner()
+	function collectionOwner() external view returns (Tuple17 memory);
+
 	/// Changes collection owner to another account
 	///
 	/// @dev Owner can be changed only by current owner
modifiedtests/src/eth/fungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -78,6 +78,15 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "addToCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "owner", "type": "address" },
       { "internalType": "address", "name": "spender", "type": "address" }
     ],
@@ -88,6 +97,15 @@
   },
   {
     "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "allowed",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "spender", "type": "address" },
       { "internalType": "uint256", "name": "amount", "type": "uint256" }
     ],
@@ -116,6 +134,23 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple6",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
@@ -261,6 +296,15 @@
     "type": "function"
   },
   {
+    "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "removeFromCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "uint8", "name": "mode", "type": "uint8" }],
     "name": "setCollectionAccess",
     "outputs": [],
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -109,6 +109,24 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "addToCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "allowed",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "approved", "type": "address" },
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
@@ -146,6 +164,23 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple17",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
@@ -376,6 +411,15 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "removeFromCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "from", "type": "address" },
       { "internalType": "address", "name": "to", "type": "address" },
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
modifiedtests/src/eth/reFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/reFungibleAbi.json
+++ b/tests/src/eth/reFungibleAbi.json
@@ -109,6 +109,24 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "addToCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
+      { "internalType": "address", "name": "user", "type": "address" }
+    ],
+    "name": "allowed",
+    "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "approved", "type": "address" },
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
     ],
@@ -146,6 +164,23 @@
     "type": "function"
   },
   {
+    "inputs": [],
+    "name": "collectionOwner",
+    "outputs": [
+      {
+        "components": [
+          { "internalType": "address", "name": "field_0", "type": "address" },
+          { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+        ],
+        "internalType": "struct Tuple17",
+        "name": "",
+        "type": "tuple"
+      }
+    ],
+    "stateMutability": "view",
+    "type": "function"
+  },
+  {
     "inputs": [{ "internalType": "string", "name": "key", "type": "string" }],
     "name": "collectionProperty",
     "outputs": [{ "internalType": "bytes", "name": "", "type": "bytes" }],
@@ -376,6 +411,15 @@
   },
   {
     "inputs": [
+      { "internalType": "uint256", "name": "user", "type": "uint256" }
+    ],
+    "name": "removeFromCollectionAllowListSubstrate",
+    "outputs": [],
+    "stateMutability": "nonpayable",
+    "type": "function"
+  },
+  {
+    "inputs": [
       { "internalType": "address", "name": "from", "type": "address" },
       { "internalType": "address", "name": "to", "type": "address" },
       { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -1651,7 +1651,7 @@
   });
 }
 
-export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId) {
+export async function isAllowlisted(api: ApiPromise, collectionId: number, address: string | CrossAccountId | IKeyringPair) {
   return (await api.rpc.unique.allowed(collectionId, normalizeAccountId(address))).toJSON();
 }
 
modifiedtests/src/util/playgrounds/unique.tsdiffbeforeafterboth
--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -730,6 +730,18 @@
   }
 
   /**
+   * Check if user is in allow list.
+   * 
+   * @param collectionId ID of collection
+   * @param user Account to check
+   * @example await getAdmins(1)
+   * @returns is user in allow list
+   */
+  async allowed(collectionId: number, user: ICrossAccountId): Promise<boolean> {
+    return (await this.helper.callRpc('api.rpc.unique.allowed', [collectionId, user])).toJSON();
+  }
+
+  /**
    * Adds an address to allow list
    * @param signer keyring of signer
    * @param collectionId ID of collection