difftreelog
Merge pull request #574 from UniqueNetwork/feature/eth_passthrought
in: master
Feature/eth passthrought
19 files changed
pallets/common/src/erc.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 solidity_interface, solidity, ToLog,21 types::*,22 execution::{Result, Error},23};24pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};25use pallet_evm_coder_substrate::dispatch_to_evm;26use sp_std::vec::Vec;27use up_data_structs::{28 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,29 SponsoringRateLimit, SponsorshipState,30};31use alloc::format;3233use crate::{34 Pallet, CollectionHandle, Config, CollectionProperties,35 eth::{convert_cross_account_to_uint256, convert_uint256_to_cross_account},36};3738/// Events for ethereum collection helper.39#[derive(ToLog)]40pub enum CollectionHelpersEvents {41 /// The collection has been created.42 CollectionCreated {43 /// Collection owner.44 #[indexed]45 owner: address,4647 /// Collection ID.48 #[indexed]49 collection_id: address,50 },51}5253/// Does not always represent a full collection, for RFT it is either54/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).55pub trait CommonEvmHandler {56 const CODE: &'static [u8];5758 /// Call precompiled handle.59 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;60}6162/// @title A contract that allows you to work with collections.63#[solidity_interface(name = Collection)]64impl<T: Config> CollectionHandle<T>65where66 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,67{68 /// Set collection property.69 ///70 /// @param key Property key.71 /// @param value Propery value.72 fn set_collection_property(73 &mut self,74 caller: caller,75 key: string,76 value: bytes,77 ) -> Result<void> {78 let caller = T::CrossAccountId::from_eth(caller);79 let key = <Vec<u8>>::from(key)80 .try_into()81 .map_err(|_| "key too large")?;82 let value = value.try_into().map_err(|_| "value too large")?;8384 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })85 .map_err(dispatch_to_evm::<T>)86 }8788 /// Delete collection property.89 ///90 /// @param key Property key.91 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {92 let caller = T::CrossAccountId::from_eth(caller);93 let key = <Vec<u8>>::from(key)94 .try_into()95 .map_err(|_| "key too large")?;9697 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)98 }99100 /// Get collection property.101 ///102 /// @dev Throws error if key not found.103 ///104 /// @param key Property key.105 /// @return bytes The property corresponding to the key.106 fn collection_property(&self, key: string) -> Result<bytes> {107 let key = <Vec<u8>>::from(key)108 .try_into()109 .map_err(|_| "key too large")?;110111 let props = <CollectionProperties<T>>::get(self.id);112 let prop = props.get(&key).ok_or("key not found")?;113114 Ok(prop.to_vec())115 }116117 /// Set the sponsor of the collection.118 ///119 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.120 ///121 /// @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> {123 check_is_owner_or_admin(caller, self)?;124125 let sponsor = T::CrossAccountId::from_eth(sponsor);126 self.set_sponsor(sponsor.as_sub().clone())127 .map_err(dispatch_to_evm::<T>)?;128 save(self)129 }130131 /// Set the substrate sponsor of the collection.132 ///133 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.134 ///135 /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.136 fn set_collection_sponsor_substrate(137 &mut self,138 caller: caller,139 sponsor: uint256,140 ) -> Result<void> {141 check_is_owner_or_admin(caller, self)?;142143 let sponsor = convert_uint256_to_cross_account::<T>(sponsor);144 self.set_sponsor(sponsor.as_sub().clone())145 .map_err(dispatch_to_evm::<T>)?;146 save(self)147 }148149 // /// Whether there is a pending sponsor.150 fn has_collection_pending_sponsor(&self) -> Result<bool> {151 Ok(matches!(152 self.collection.sponsorship,153 SponsorshipState::Unconfirmed(_)154 ))155 }156157 /// Collection sponsorship confirmation.158 ///159 /// @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> {161 let caller = T::CrossAccountId::from_eth(caller);162 if !self163 .confirm_sponsorship(caller.as_sub())164 .map_err(dispatch_to_evm::<T>)?165 {166 return Err("caller is not set as sponsor".into());167 }168 save(self)169 }170171 /// Remove collection sponsor.172 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {173 check_is_owner_or_admin(caller, self)?;174 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;175 save(self)176 }177178 /// Get current sponsor.179 ///180 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.181 fn get_collection_sponsor(&self) -> Result<(address, uint256)> {182 let sponsor = match self.collection.sponsorship.sponsor() {183 Some(sponsor) => sponsor,184 None => return Ok(Default::default()),185 };186 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());187 let result: (address, uint256) = if sponsor.is_canonical_substrate() {188 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);189 (Default::default(), sponsor)190 } else {191 let sponsor = *sponsor.as_eth();192 (sponsor, Default::default())193 };194 Ok(result)195 }196197 /// Set limits for the collection.198 /// @dev Throws error if limit not found.199 /// @param limit Name of the limit. Valid names:200 /// "accountTokenOwnershipLimit",201 /// "sponsoredDataSize",202 /// "sponsoredDataRateLimit",203 /// "tokenLimit",204 /// "sponsorTransferTimeout",205 /// "sponsorApproveTimeout"206 /// @param value Value of the limit.207 #[solidity(rename_selector = "setCollectionLimit")]208 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {209 check_is_owner_or_admin(caller, self)?;210 let mut limits = self.limits.clone();211212 match limit.as_str() {213 "accountTokenOwnershipLimit" => {214 limits.account_token_ownership_limit = Some(value);215 }216 "sponsoredDataSize" => {217 limits.sponsored_data_size = Some(value);218 }219 "sponsoredDataRateLimit" => {220 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));221 }222 "tokenLimit" => {223 limits.token_limit = Some(value);224 }225 "sponsorTransferTimeout" => {226 limits.sponsor_transfer_timeout = Some(value);227 }228 "sponsorApproveTimeout" => {229 limits.sponsor_approve_timeout = Some(value);230 }231 _ => {232 return Err(Error::Revert(format!(233 "unknown integer limit \"{}\"",234 limit235 )))236 }237 }238 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)239 .map_err(dispatch_to_evm::<T>)?;240 save(self)241 }242243 /// Set limits for the collection.244 /// @dev Throws error if limit not found.245 /// @param limit Name of the limit. Valid names:246 /// "ownerCanTransfer",247 /// "ownerCanDestroy",248 /// "transfersEnabled"249 /// @param value Value of the limit.250 #[solidity(rename_selector = "setCollectionLimit")]251 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {252 check_is_owner_or_admin(caller, self)?;253 let mut limits = self.limits.clone();254255 match limit.as_str() {256 "ownerCanTransfer" => {257 limits.owner_can_transfer = Some(value);258 }259 "ownerCanDestroy" => {260 limits.owner_can_destroy = Some(value);261 }262 "transfersEnabled" => {263 limits.transfers_enabled = Some(value);264 }265 _ => {266 return Err(Error::Revert(format!(267 "unknown boolean limit \"{}\"",268 limit269 )))270 }271 }272 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)273 .map_err(dispatch_to_evm::<T>)?;274 save(self)275 }276277 /// Get contract address.278 fn contract_address(&self, _caller: caller) -> Result<address> {279 Ok(crate::eth::collection_id_to_address(self.id))280 }281282 /// Add collection admin by substrate address.283 /// @param newAdmin Substrate administrator address.284 fn add_collection_admin_substrate(285 &mut self,286 caller: caller,287 new_admin: uint256,288 ) -> Result<void> {289 let caller = T::CrossAccountId::from_eth(caller);290 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>)?;292 Ok(())293 }294295 /// Remove collection admin by substrate address.296 /// @param admin Substrate administrator address.297 fn remove_collection_admin_substrate(298 &mut self,299 caller: caller,300 admin: uint256,301 ) -> Result<void> {302 let caller = T::CrossAccountId::from_eth(caller);303 let admin = convert_uint256_to_cross_account::<T>(admin);304 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;305 Ok(())306 }307308 /// Add collection admin.309 /// @param newAdmin Address of the added administrator.310 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {311 let caller = T::CrossAccountId::from_eth(caller);312 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>)?;314 Ok(())315 }316317 /// Remove collection admin.318 ///319 /// @param admin Address of the removed administrator.320 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {321 let caller = T::CrossAccountId::from_eth(caller);322 let admin = T::CrossAccountId::from_eth(admin);323 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;324 Ok(())325 }326327 /// Toggle accessibility of collection nesting.328 ///329 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'330 #[solidity(rename_selector = "setCollectionNesting")]331 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {332 check_is_owner_or_admin(caller, self)?;333334 let mut permissions = self.collection.permissions.clone();335 let mut nesting = permissions.nesting().clone();336 nesting.token_owner = enable;337 nesting.restricted = None;338 permissions.nesting = Some(nesting);339340 self.collection.permissions = <Pallet<T>>::clamp_permissions(341 self.collection.mode.clone(),342 &self.collection.permissions,343 permissions,344 )345 .map_err(dispatch_to_evm::<T>)?;346347 save(self)348 }349350 /// Toggle accessibility of collection nesting.351 ///352 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'353 /// @param collections Addresses of collections that will be available for nesting.354 #[solidity(rename_selector = "setCollectionNesting")]355 fn set_nesting(356 &mut self,357 caller: caller,358 enable: bool,359 collections: Vec<address>,360 ) -> Result<void> {361 if collections.is_empty() {362 return Err("no addresses provided".into());363 }364 check_is_owner_or_admin(caller, self)?;365366 let mut permissions = self.collection.permissions.clone();367 match enable {368 false => {369 let mut nesting = permissions.nesting().clone();370 nesting.token_owner = false;371 nesting.restricted = None;372 permissions.nesting = Some(nesting);373 }374 true => {375 let mut bv = OwnerRestrictedSet::new();376 for i in collections {377 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or(Error::Revert(378 "Can't convert address into collection id".into(),379 ))?)380 .map_err(|_| "too many collections")?;381 }382 let mut nesting = permissions.nesting().clone();383 nesting.token_owner = true;384 nesting.restricted = Some(bv);385 permissions.nesting = Some(nesting);386 }387 };388389 self.collection.permissions = <Pallet<T>>::clamp_permissions(390 self.collection.mode.clone(),391 &self.collection.permissions,392 permissions,393 )394 .map_err(dispatch_to_evm::<T>)?;395396 save(self)397 }398399 /// Set the collection access method.400 /// @param mode Access mode401 /// 0 for Normal402 /// 1 for AllowList403 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {404 check_is_owner_or_admin(caller, self)?;405 let permissions = CollectionPermissions {406 access: Some(match mode {407 0 => AccessMode::Normal,408 1 => AccessMode::AllowList,409 _ => return Err("not supported access mode".into()),410 }),411 ..Default::default()412 };413 self.collection.permissions = <Pallet<T>>::clamp_permissions(414 self.collection.mode.clone(),415 &self.collection.permissions,416 permissions,417 )418 .map_err(dispatch_to_evm::<T>)?;419420 save(self)421 }422423 /// Add the user to the allowed list.424 ///425 /// @param user Address of a trusted user.426 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {427 let caller = T::CrossAccountId::from_eth(caller);428 let user = T::CrossAccountId::from_eth(user);429 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;430 Ok(())431 }432433 /// Remove the user from the allowed list.434 ///435 /// @param user Address of a removed user.436 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {437 let caller = T::CrossAccountId::from_eth(caller);438 let user = T::CrossAccountId::from_eth(user);439 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;440 Ok(())441 }442443 /// Switch permission for minting.444 ///445 /// @param mode Enable if "true".446 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {447 check_is_owner_or_admin(caller, self)?;448 let permissions = CollectionPermissions {449 mint_mode: Some(mode),450 ..Default::default()451 };452 self.collection.permissions = <Pallet<T>>::clamp_permissions(453 self.collection.mode.clone(),454 &self.collection.permissions,455 permissions,456 )457 .map_err(dispatch_to_evm::<T>)?;458459 save(self)460 }461462 /// Check that account is the owner or admin of the collection463 ///464 /// @param user account to verify465 /// @return "true" if account is the owner or admin466 #[solidity(rename_selector = "isOwnerOrAdmin")]467 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {468 let user = T::CrossAccountId::from_eth(user);469 Ok(self.is_owner_or_admin(&user))470 }471472 /// Check that substrate account is the owner or admin of the collection473 ///474 /// @param user account to verify475 /// @return "true" if account is the owner or admin476 fn is_owner_or_admin_substrate(&self, user: uint256) -> Result<bool> {477 let user = convert_uint256_to_cross_account::<T>(user);478 Ok(self.is_owner_or_admin(&user))479 }480481 /// Returns collection type482 ///483 /// @return `Fungible` or `NFT` or `ReFungible`484 fn unique_collection_type(&mut self) -> Result<string> {485 let mode = match self.collection.mode {486 CollectionMode::Fungible(_) => "Fungible",487 CollectionMode::NFT => "NFT",488 CollectionMode::ReFungible => "ReFungible",489 };490 Ok(mode.into())491 }492493 /// Changes collection owner to another account494 ///495 /// @dev Owner can be changed only by current owner496 /// @param newOwner new owner account497 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {498 let caller = T::CrossAccountId::from_eth(caller);499 let new_owner = T::CrossAccountId::from_eth(new_owner);500 self.set_owner_internal(caller, new_owner)501 .map_err(dispatch_to_evm::<T>)502 }503504 /// Changes collection owner to another substrate account505 ///506 /// @dev Owner can be changed only by current owner507 /// @param newOwner new owner substrate account508 fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {509 let caller = T::CrossAccountId::from_eth(caller);510 let new_owner = convert_uint256_to_cross_account::<T>(new_owner);511 self.set_owner_internal(caller, new_owner)512 .map_err(dispatch_to_evm::<T>)513 }514}515516fn check_is_owner_or_admin<T: Config>(517 caller: caller,518 collection: &CollectionHandle<T>,519) -> Result<T::CrossAccountId> {520 let caller = T::CrossAccountId::from_eth(caller);521 collection522 .check_is_owner_or_admin(&caller)523 .map_err(dispatch_to_evm::<T>)?;524 Ok(caller)525}526527fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {528 // TODO possibly delete for the lack of transaction529 collection.consume_store_writes(1)?;530 collection531 .check_is_internal()532 .map_err(dispatch_to_evm::<T>)?;533 collection.save().map_err(dispatch_to_evm::<T>)?;534 Ok(())535}536537/// Contains static property keys and values.538pub mod static_property {539 use evm_coder::{540 execution::{Result, Error},541 };542 use alloc::format;543544 const EXPECT_CONVERT_ERROR: &str = "length < limit";545546 /// Keys.547 pub mod key {548 use super::*;549550 /// Key "schemaName".551 pub fn schema_name() -> up_data_structs::PropertyKey {552 property_key_from_bytes(b"schemaName").expect(EXPECT_CONVERT_ERROR)553 }554555 /// Key "baseURI".556 pub fn base_uri() -> up_data_structs::PropertyKey {557 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)558 }559560 /// Key "url".561 pub fn url() -> up_data_structs::PropertyKey {562 property_key_from_bytes(b"url").expect(EXPECT_CONVERT_ERROR)563 }564565 /// Key "suffix".566 pub fn suffix() -> up_data_structs::PropertyKey {567 property_key_from_bytes(b"suffix").expect(EXPECT_CONVERT_ERROR)568 }569570 /// Key "parentNft".571 pub fn parent_nft() -> up_data_structs::PropertyKey {572 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)573 }574 }575576 /// Values.577 pub mod value {578 use super::*;579580 /// Value "ERC721Metadata".581 pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";582583 /// Value for [`ERC721_METADATA`].584 pub fn erc721() -> up_data_structs::PropertyValue {585 property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)586 }587 }588589 /// Convert `byte` to [`PropertyKey`].590 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {591 bytes.to_vec().try_into().map_err(|_| {592 Error::Revert(format!(593 "Property key is too long. Max length is {}.",594 up_data_structs::PropertyKey::bound()595 ))596 })597 }598599 /// Convert `bytes` to [`PropertyValue`].600 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {601 bytes.to_vec().try_into().map_err(|_| {602 Error::Revert(format!(603 "Property key is too long. Max length is {}.",604 up_data_structs::PropertyKey::bound()605 ))606 })607 }608}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819use evm_coder::{20 solidity_interface, solidity, ToLog,21 types::*,22 execution::{Result, Error},23 weight,24};25pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};26use pallet_evm_coder_substrate::dispatch_to_evm;27use sp_std::vec::Vec;28use up_data_structs::{29 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,30 SponsoringRateLimit, SponsorshipState,31};32use alloc::format;3334use crate::{35 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,36 eth::{37 convert_cross_account_to_uint256, convert_uint256_to_cross_account,38 convert_cross_account_to_tuple,39 },40 weights::WeightInfo,41};4243/// Events for ethereum collection helper.44#[derive(ToLog)]45pub enum CollectionHelpersEvents {46 /// The collection has been created.47 CollectionCreated {48 /// Collection owner.49 #[indexed]50 owner: address,5152 /// Collection ID.53 #[indexed]54 collection_id: address,55 },56}5758/// Does not always represent a full collection, for RFT it is either59/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).60pub trait CommonEvmHandler {61 const CODE: &'static [u8];6263 /// Call precompiled handle.64 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;65}6667/// @title A contract that allows you to work with collections.68#[solidity_interface(name = Collection)]69impl<T: Config> CollectionHandle<T>70where71 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,72{73 /// Set collection property.74 ///75 /// @param key Property key.76 /// @param value Propery value.77 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]78 fn set_collection_property(79 &mut self,80 caller: caller,81 key: string,82 value: bytes,83 ) -> Result<void> {84 let caller = T::CrossAccountId::from_eth(caller);85 let key = <Vec<u8>>::from(key)86 .try_into()87 .map_err(|_| "key too large")?;88 let value = value.try_into().map_err(|_| "value too large")?;8990 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })91 .map_err(dispatch_to_evm::<T>)92 }9394 /// Delete collection property.95 ///96 /// @param key Property key.97 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]98 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {99 self.consume_store_reads_and_writes(1, 1)?;100101 let caller = T::CrossAccountId::from_eth(caller);102 let key = <Vec<u8>>::from(key)103 .try_into()104 .map_err(|_| "key too large")?;105106 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)107 }108109 /// Get collection property.110 ///111 /// @dev Throws error if key not found.112 ///113 /// @param key Property key.114 /// @return bytes The property corresponding to the key.115 fn collection_property(&self, key: string) -> Result<bytes> {116 let key = <Vec<u8>>::from(key)117 .try_into()118 .map_err(|_| "key too large")?;119120 let props = <CollectionProperties<T>>::get(self.id);121 let prop = props.get(&key).ok_or("key not found")?;122123 Ok(prop.to_vec())124 }125126 /// Set the sponsor of the collection.127 ///128 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.129 ///130 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.131 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {132 self.consume_store_reads_and_writes(1, 1)?;133134 check_is_owner_or_admin(caller, self)?;135136 let sponsor = T::CrossAccountId::from_eth(sponsor);137 self.set_sponsor(sponsor.as_sub().clone())138 .map_err(dispatch_to_evm::<T>)?;139 save(self)140 }141142 /// Set the substrate sponsor of the collection.143 ///144 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.145 ///146 /// @param sponsor Substrate address of the sponsor from whose account funds will be debited for operations with the contract.147 fn set_collection_sponsor_substrate(148 &mut self,149 caller: caller,150 sponsor: uint256,151 ) -> Result<void> {152 self.consume_store_reads_and_writes(1, 1)?;153154 check_is_owner_or_admin(caller, self)?;155156 let sponsor = convert_uint256_to_cross_account::<T>(sponsor);157 self.set_sponsor(sponsor.as_sub().clone())158 .map_err(dispatch_to_evm::<T>)?;159 save(self)160 }161162 /// Whether there is a pending sponsor.163 fn has_collection_pending_sponsor(&self) -> Result<bool> {164 Ok(matches!(165 self.collection.sponsorship,166 SponsorshipState::Unconfirmed(_)167 ))168 }169170 /// Collection sponsorship confirmation.171 ///172 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.173 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {174 self.consume_store_writes(1)?;175176 let caller = T::CrossAccountId::from_eth(caller);177 if !self178 .confirm_sponsorship(caller.as_sub())179 .map_err(dispatch_to_evm::<T>)?180 {181 return Err("caller is not set as sponsor".into());182 }183 save(self)184 }185186 /// Remove collection sponsor.187 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {188 self.consume_store_reads_and_writes(1, 1)?;189 check_is_owner_or_admin(caller, self)?;190 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;191 save(self)192 }193194 /// Get current sponsor.195 ///196 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.197 fn get_collection_sponsor(&self) -> Result<(address, uint256)> {198 let sponsor = match self.collection.sponsorship.sponsor() {199 Some(sponsor) => sponsor,200 None => return Ok(Default::default()),201 };202 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());203 let result: (address, uint256) = if sponsor.is_canonical_substrate() {204 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);205 (Default::default(), sponsor)206 } else {207 let sponsor = *sponsor.as_eth();208 (sponsor, Default::default())209 };210 Ok(result)211 }212213 /// Set limits for the collection.214 /// @dev Throws error if limit not found.215 /// @param limit Name of the limit. Valid names:216 /// "accountTokenOwnershipLimit",217 /// "sponsoredDataSize",218 /// "sponsoredDataRateLimit",219 /// "tokenLimit",220 /// "sponsorTransferTimeout",221 /// "sponsorApproveTimeout"222 /// @param value Value of the limit.223 #[solidity(rename_selector = "setCollectionLimit")]224 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {225 self.consume_store_reads_and_writes(1, 1)?;226227 check_is_owner_or_admin(caller, self)?;228 let mut limits = self.limits.clone();229230 match limit.as_str() {231 "accountTokenOwnershipLimit" => {232 limits.account_token_ownership_limit = Some(value);233 }234 "sponsoredDataSize" => {235 limits.sponsored_data_size = Some(value);236 }237 "sponsoredDataRateLimit" => {238 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));239 }240 "tokenLimit" => {241 limits.token_limit = Some(value);242 }243 "sponsorTransferTimeout" => {244 limits.sponsor_transfer_timeout = Some(value);245 }246 "sponsorApproveTimeout" => {247 limits.sponsor_approve_timeout = Some(value);248 }249 _ => {250 return Err(Error::Revert(format!(251 "unknown integer limit \"{}\"",252 limit253 )))254 }255 }256 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)257 .map_err(dispatch_to_evm::<T>)?;258 save(self)259 }260261 /// Set limits for the collection.262 /// @dev Throws error if limit not found.263 /// @param limit Name of the limit. Valid names:264 /// "ownerCanTransfer",265 /// "ownerCanDestroy",266 /// "transfersEnabled"267 /// @param value Value of the limit.268 #[solidity(rename_selector = "setCollectionLimit")]269 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {270 self.consume_store_reads_and_writes(1, 1)?;271272 check_is_owner_or_admin(caller, self)?;273 let mut limits = self.limits.clone();274275 match limit.as_str() {276 "ownerCanTransfer" => {277 limits.owner_can_transfer = Some(value);278 }279 "ownerCanDestroy" => {280 limits.owner_can_destroy = Some(value);281 }282 "transfersEnabled" => {283 limits.transfers_enabled = Some(value);284 }285 _ => {286 return Err(Error::Revert(format!(287 "unknown boolean limit \"{}\"",288 limit289 )))290 }291 }292 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)293 .map_err(dispatch_to_evm::<T>)?;294 save(self)295 }296297 /// Get contract address.298 fn contract_address(&self) -> Result<address> {299 Ok(crate::eth::collection_id_to_address(self.id))300 }301302 /// Add collection admin by substrate address.303 /// @param newAdmin Substrate administrator address.304 fn add_collection_admin_substrate(305 &mut self,306 caller: caller,307 new_admin: uint256,308 ) -> Result<void> {309 self.consume_store_writes(2)?;310311 let caller = T::CrossAccountId::from_eth(caller);312 let new_admin = convert_uint256_to_cross_account::<T>(new_admin);313 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;314 Ok(())315 }316317 /// Remove collection admin by substrate address.318 /// @param admin Substrate administrator address.319 fn remove_collection_admin_substrate(320 &mut self,321 caller: caller,322 admin: uint256,323 ) -> Result<void> {324 self.consume_store_writes(2)?;325326 let caller = T::CrossAccountId::from_eth(caller);327 let admin = convert_uint256_to_cross_account::<T>(admin);328 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;329 Ok(())330 }331332 /// Add collection admin.333 /// @param newAdmin Address of the added administrator.334 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {335 self.consume_store_writes(2)?;336337 let caller = T::CrossAccountId::from_eth(caller);338 let new_admin = T::CrossAccountId::from_eth(new_admin);339 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;340 Ok(())341 }342343 /// Remove collection admin.344 ///345 /// @param admin Address of the removed administrator.346 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {347 self.consume_store_writes(2)?;348349 let caller = T::CrossAccountId::from_eth(caller);350 let admin = T::CrossAccountId::from_eth(admin);351 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;352 Ok(())353 }354355 /// Toggle accessibility of collection nesting.356 ///357 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'358 #[solidity(rename_selector = "setCollectionNesting")]359 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {360 self.consume_store_reads_and_writes(1, 1)?;361362 check_is_owner_or_admin(caller, self)?;363364 let mut permissions = self.collection.permissions.clone();365 let mut nesting = permissions.nesting().clone();366 nesting.token_owner = enable;367 nesting.restricted = None;368 permissions.nesting = Some(nesting);369370 self.collection.permissions = <Pallet<T>>::clamp_permissions(371 self.collection.mode.clone(),372 &self.collection.permissions,373 permissions,374 )375 .map_err(dispatch_to_evm::<T>)?;376377 save(self)378 }379380 /// Toggle accessibility of collection nesting.381 ///382 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'383 /// @param collections Addresses of collections that will be available for nesting.384 #[solidity(rename_selector = "setCollectionNesting")]385 fn set_nesting(386 &mut self,387 caller: caller,388 enable: bool,389 collections: Vec<address>,390 ) -> Result<void> {391 self.consume_store_reads_and_writes(1, 1)?;392393 if collections.is_empty() {394 return Err("no addresses provided".into());395 }396 check_is_owner_or_admin(caller, self)?;397398 let mut permissions = self.collection.permissions.clone();399 match enable {400 false => {401 let mut nesting = permissions.nesting().clone();402 nesting.token_owner = false;403 nesting.restricted = None;404 permissions.nesting = Some(nesting);405 }406 true => {407 let mut bv = OwnerRestrictedSet::new();408 for i in collections {409 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or(Error::Revert(410 "Can't convert address into collection id".into(),411 ))?)412 .map_err(|_| "too many collections")?;413 }414 let mut nesting = permissions.nesting().clone();415 nesting.token_owner = true;416 nesting.restricted = Some(bv);417 permissions.nesting = Some(nesting);418 }419 };420421 self.collection.permissions = <Pallet<T>>::clamp_permissions(422 self.collection.mode.clone(),423 &self.collection.permissions,424 permissions,425 )426 .map_err(dispatch_to_evm::<T>)?;427428 save(self)429 }430431 /// Set the collection access method.432 /// @param mode Access mode433 /// 0 for Normal434 /// 1 for AllowList435 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {436 self.consume_store_reads_and_writes(1, 1)?;437438 check_is_owner_or_admin(caller, self)?;439 let permissions = CollectionPermissions {440 access: Some(match mode {441 0 => AccessMode::Normal,442 1 => AccessMode::AllowList,443 _ => return Err("not supported access mode".into()),444 }),445 ..Default::default()446 };447 self.collection.permissions = <Pallet<T>>::clamp_permissions(448 self.collection.mode.clone(),449 &self.collection.permissions,450 permissions,451 )452 .map_err(dispatch_to_evm::<T>)?;453454 save(self)455 }456457 /// 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 }466467 /// Add the user to the allowed list.468 ///469 /// @param user Address of a trusted user.470 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {471 self.consume_store_writes(1)?;472473 let caller = T::CrossAccountId::from_eth(caller);474 let user = T::CrossAccountId::from_eth(user);475 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;476 Ok(())477 }478479 /// 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)?;488489 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 }494495 /// Remove the user from the allowed list.496 ///497 /// @param user Address of a removed user.498 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {499 self.consume_store_writes(1)?;500501 let caller = T::CrossAccountId::from_eth(caller);502 let user = T::CrossAccountId::from_eth(user);503 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;504 Ok(())505 }506507 /// 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)?;516517 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 }522523 /// Switch permission for minting.524 ///525 /// @param mode Enable if "true".526 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {527 self.consume_store_reads_and_writes(1, 1)?;528529 check_is_owner_or_admin(caller, self)?;530 let permissions = CollectionPermissions {531 mint_mode: Some(mode),532 ..Default::default()533 };534 self.collection.permissions = <Pallet<T>>::clamp_permissions(535 self.collection.mode.clone(),536 &self.collection.permissions,537 permissions,538 )539 .map_err(dispatch_to_evm::<T>)?;540541 save(self)542 }543544 /// Check that account is the owner or admin of the collection545 ///546 /// @param user account to verify547 /// @return "true" if account is the owner or admin548 #[solidity(rename_selector = "isOwnerOrAdmin")]549 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {550 let user = T::CrossAccountId::from_eth(user);551 Ok(self.is_owner_or_admin(&user))552 }553554 /// Check that substrate account is the owner or admin of the collection555 ///556 /// @param user account to verify557 /// @return "true" if account is the owner or admin558 fn is_owner_or_admin_substrate(&self, user: uint256) -> Result<bool> {559 let user = convert_uint256_to_cross_account::<T>(user);560 Ok(self.is_owner_or_admin(&user))561 }562563 /// Returns collection type564 ///565 /// @return `Fungible` or `NFT` or `ReFungible`566 fn unique_collection_type(&self) -> Result<string> {567 let mode = match self.collection.mode {568 CollectionMode::Fungible(_) => "Fungible",569 CollectionMode::NFT => "NFT",570 CollectionMode::ReFungible => "ReFungible",571 };572 Ok(mode.into())573 }574575 /// 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 }584585 /// Changes collection owner to another account586 ///587 /// @dev Owner can be changed only by current owner588 /// @param newOwner new owner account589 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {590 self.consume_store_writes(1)?;591592 let caller = T::CrossAccountId::from_eth(caller);593 let new_owner = T::CrossAccountId::from_eth(new_owner);594 self.set_owner_internal(caller, new_owner)595 .map_err(dispatch_to_evm::<T>)596 }597598 /// Changes collection owner to another substrate account599 ///600 /// @dev Owner can be changed only by current owner601 /// @param newOwner new owner substrate account602 fn set_owner_substrate(&mut self, caller: caller, new_owner: uint256) -> Result<void> {603 self.consume_store_writes(1)?;604605 let caller = T::CrossAccountId::from_eth(caller);606 let new_owner = convert_uint256_to_cross_account::<T>(new_owner);607 self.set_owner_internal(caller, new_owner)608 .map_err(dispatch_to_evm::<T>)609 }610611 // 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 // }618}619620/// ### Note621/// Do not forget to add: `self.consume_store_reads(1)?;`622fn check_is_owner_or_admin<T: Config>(623 caller: caller,624 collection: &CollectionHandle<T>,625) -> Result<T::CrossAccountId> {626 let caller = T::CrossAccountId::from_eth(caller);627 collection628 .check_is_owner_or_admin(&caller)629 .map_err(dispatch_to_evm::<T>)?;630 Ok(caller)631}632633/// ### Note634/// Do not forget to add: `self.consume_store_writes(1)?;`635fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {636 collection637 .check_is_internal()638 .map_err(dispatch_to_evm::<T>)?;639 collection.save().map_err(dispatch_to_evm::<T>)?;640 Ok(())641}642643/// Contains static property keys and values.644pub mod static_property {645 use evm_coder::{646 execution::{Result, Error},647 };648 use alloc::format;649650 const EXPECT_CONVERT_ERROR: &str = "length < limit";651652 /// Keys.653 pub mod key {654 use super::*;655656 /// Key "schemaName".657 pub fn schema_name() -> up_data_structs::PropertyKey {658 property_key_from_bytes(b"schemaName").expect(EXPECT_CONVERT_ERROR)659 }660661 /// Key "baseURI".662 pub fn base_uri() -> up_data_structs::PropertyKey {663 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)664 }665666 /// Key "url".667 pub fn url() -> up_data_structs::PropertyKey {668 property_key_from_bytes(b"url").expect(EXPECT_CONVERT_ERROR)669 }670671 /// Key "suffix".672 pub fn suffix() -> up_data_structs::PropertyKey {673 property_key_from_bytes(b"suffix").expect(EXPECT_CONVERT_ERROR)674 }675676 /// Key "parentNft".677 pub fn parent_nft() -> up_data_structs::PropertyKey {678 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)679 }680 }681682 /// Values.683 pub mod value {684 use super::*;685686 /// Value "ERC721Metadata".687 pub const ERC721_METADATA: &[u8] = b"ERC721Metadata";688689 /// Value for [`ERC721_METADATA`].690 pub fn erc721() -> up_data_structs::PropertyValue {691 property_value_from_bytes(ERC721_METADATA).expect(EXPECT_CONVERT_ERROR)692 }693 }694695 /// Convert `byte` to [`PropertyKey`].696 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {697 bytes.to_vec().try_into().map_err(|_| {698 Error::Revert(format!(699 "Property key is too long. Max length is {}.",700 up_data_structs::PropertyKey::bound()701 ))702 })703 }704705 /// Convert `bytes` to [`PropertyValue`].706 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {707 bytes.to_vec().try_into().map_err(|_| {708 Error::Revert(format!(709 "Property key is too long. Max length is {}.",710 up_data_structs::PropertyKey::bound()711 ))712 })713 }714}pallets/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())
+ }
+}
pallets/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,
pallets/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.
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/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
pallets/nonfungible/src/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/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
pallets/refungible/src/stubs/UniqueRefungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/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
tests/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;
+ });
+});
tests/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
tests/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
tests/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
tests/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": [],
tests/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" }
tests/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" }
tests/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();
}
tests/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