difftreelog
chore fix cargo check warnings
in: master
10 files changed
Cargo.tomldiffbeforeafterboth--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,3 @@
-cargo-features = ["workspace-inheritance"]
-
[workspace]
resolver = "2"
members = [
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.1819pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};20use evm_coder::{21 abi::AbiType,22 solidity_interface, solidity, ToLog,23 types::*,24 types::Property as PropertyStruct,25 execution::{Result, Error},26 weight,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::{vec, vec::Vec};30use up_data_structs::{31 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,32 SponsoringRateLimit, SponsorshipState,33};34use alloc::format;3536use crate::{37 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,38 eth::{39 EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions,40 },41 weights::WeightInfo,42};4344/// Events for ethereum collection helper.45#[derive(ToLog)]46pub enum CollectionHelpersEvents {47 /// The collection has been created.48 CollectionCreated {49 /// Collection owner.50 #[indexed]51 owner: address,5253 /// Collection ID.54 #[indexed]55 collection_id: address,56 },57 /// The collection has been destroyed.58 CollectionDestroyed {59 /// Collection ID.60 #[indexed]61 collection_id: address,62 },63 /// The collection has been changed.64 CollectionChanged {65 /// Collection ID.66 #[indexed]67 collection_id: address,68 },6970 /// The token has been changed.71 TokenChanged {72 /// Collection ID.73 #[indexed]74 collection_id: address,75 /// Token ID.76 token_id: uint256,77 },78}7980/// Does not always represent a full collection, for RFT it is either81/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).82pub trait CommonEvmHandler {83 /// Raw compiled binary code of the contract stub84 const CODE: &'static [u8];8586 /// Call precompiled handle.87 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;88}8990/// @title A contract that allows you to work with collections.91#[solidity_interface(name = Collection)]92impl<T: Config> CollectionHandle<T>93where94 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,95{96 /// Set collection property.97 ///98 /// @param key Property key.99 /// @param value Propery value.100 #[solidity(hide)]101 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]102 fn set_collection_property(103 &mut self,104 caller: caller,105 key: string,106 value: bytes,107 ) -> Result<void> {108 let caller = T::CrossAccountId::from_eth(caller);109 let key = <Vec<u8>>::from(key)110 .try_into()111 .map_err(|_| "key too large")?;112 let value = value.0.try_into().map_err(|_| "value too large")?;113114 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })115 .map_err(dispatch_to_evm::<T>)116 }117118 /// Set collection properties.119 ///120 /// @param properties Vector of properties key/value pair.121 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]122 fn set_collection_properties(123 &mut self,124 caller: caller,125 properties: Vec<PropertyStruct>,126 ) -> Result<void> {127 let caller = T::CrossAccountId::from_eth(caller);128129 let properties = properties130 .into_iter()131 .map(|PropertyStruct { key, value }| {132 let key = <Vec<u8>>::from(key)133 .try_into()134 .map_err(|_| "key too large")?;135136 let value = value.0.try_into().map_err(|_| "value too large")?;137138 Ok(Property { key, value })139 })140 .collect::<Result<Vec<_>>>()?;141142 <Pallet<T>>::set_collection_properties(self, &caller, properties)143 .map_err(dispatch_to_evm::<T>)144 }145146 /// Delete collection property.147 ///148 /// @param key Property key.149 #[solidity(hide)]150 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]151 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {152 let caller = T::CrossAccountId::from_eth(caller);153 let key = <Vec<u8>>::from(key)154 .try_into()155 .map_err(|_| "key too large")?;156157 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)158 }159160 /// Delete collection properties.161 ///162 /// @param keys Properties keys.163 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]164 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {165 let caller = T::CrossAccountId::from_eth(caller);166 let keys = keys167 .into_iter()168 .map(|key| {169 <Vec<u8>>::from(key)170 .try_into()171 .map_err(|_| Error::Revert("key too large".into()))172 })173 .collect::<Result<Vec<_>>>()?;174175 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)176 }177178 /// Get collection property.179 ///180 /// @dev Throws error if key not found.181 ///182 /// @param key Property key.183 /// @return bytes The property corresponding to the key.184 fn collection_property(&self, key: string) -> Result<bytes> {185 let key = <Vec<u8>>::from(key)186 .try_into()187 .map_err(|_| "key too large")?;188189 let props = CollectionProperties::<T>::get(self.id);190 let prop = props.get(&key).ok_or("key not found")?;191192 Ok(bytes(prop.to_vec()))193 }194195 /// Get collection properties.196 ///197 /// @param keys Properties keys. Empty keys for all propertyes.198 /// @return Vector of properties key/value pairs.199 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<PropertyStruct>> {200 let keys = keys201 .into_iter()202 .map(|key| {203 <Vec<u8>>::from(key)204 .try_into()205 .map_err(|_| Error::Revert("key too large".into()))206 })207 .collect::<Result<Vec<_>>>()?;208209 let properties = Pallet::<T>::filter_collection_properties(210 self.id,211 if keys.is_empty() { None } else { Some(keys) },212 )213 .map_err(dispatch_to_evm::<T>)?;214215 let properties = properties216 .into_iter()217 .map(|p| {218 let key =219 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;220 let value = bytes(p.value.to_vec());221 Ok(PropertyStruct { key, value })222 })223 .collect::<Result<Vec<_>>>()?;224 Ok(properties)225 }226227 /// Set the sponsor of the collection.228 ///229 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.230 ///231 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.232 #[solidity(hide)]233 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {234 self.consume_store_reads_and_writes(1, 1)?;235236 let caller = T::CrossAccountId::from_eth(caller);237238 let sponsor = T::CrossAccountId::from_eth(sponsor);239 self.set_sponsor(&caller, sponsor.as_sub().clone())240 .map_err(dispatch_to_evm::<T>)241 }242243 /// Set the sponsor of the collection.244 ///245 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.246 ///247 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.248 fn set_collection_sponsor_cross(249 &mut self,250 caller: caller,251 sponsor: EthCrossAccount,252 ) -> Result<void> {253 self.consume_store_reads_and_writes(1, 1)?;254255 let caller = T::CrossAccountId::from_eth(caller);256257 let sponsor = sponsor.into_sub_cross_account::<T>()?;258 self.set_sponsor(&caller, sponsor.as_sub().clone())259 .map_err(dispatch_to_evm::<T>)260 }261262 /// Whether there is a pending sponsor.263 fn has_collection_pending_sponsor(&self) -> Result<bool> {264 Ok(matches!(265 self.collection.sponsorship,266 SponsorshipState::Unconfirmed(_)267 ))268 }269270 /// Collection sponsorship confirmation.271 ///272 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.273 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {274 self.consume_store_writes(1)?;275276 let caller = T::CrossAccountId::from_eth(caller);277 self.confirm_sponsorship(caller.as_sub())278 .map_err(dispatch_to_evm::<T>)279 }280281 /// Remove collection sponsor.282 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {283 self.consume_store_reads_and_writes(1, 1)?;284 let caller = T::CrossAccountId::from_eth(caller);285 self.remove_sponsor(&caller).map_err(dispatch_to_evm::<T>)286 }287288 /// Get current sponsor.289 ///290 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.291 fn collection_sponsor(&self) -> Result<(address, uint256)> {292 let sponsor = match self.collection.sponsorship.sponsor() {293 Some(sponsor) => sponsor,294 None => return Ok(Default::default()),295 };296 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());297 let result: (address, uint256) = if sponsor.is_canonical_substrate() {298 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);299 (Default::default(), sponsor)300 } else {301 let sponsor = *sponsor.as_eth();302 (sponsor, Default::default())303 };304 Ok(result)305 }306307 /// Set limits for the collection.308 /// @dev Throws error if limit not found.309 /// @param limit Name of the limit. Valid names:310 /// "accountTokenOwnershipLimit",311 /// "sponsoredDataSize",312 /// "sponsoredDataRateLimit",313 /// "tokenLimit",314 /// "sponsorTransferTimeout",315 /// "sponsorApproveTimeout"316 /// "ownerCanTransfer",317 /// "ownerCanDestroy",318 /// "transfersEnabled"319 /// @param value Value of the limit.320 #[solidity(rename_selector = "setCollectionLimit")]321 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint256) -> Result<void> {322 self.consume_store_reads_and_writes(1, 1)?;323324 let value = value325 .try_into()326 .map_err(|_| Error::Revert(format!("can't convert value to u32 \"{}\"", value)))?;327328 let convert_value_to_bool = || match value {329 0 => Ok(false),330 1 => Ok(true),331 _ => {332 return Err(Error::Revert(format!(333 "can't convert value to boolean \"{}\"",334 value335 )))336 }337 };338339 let mut limits = self.limits.clone();340341 match limit.as_str() {342 "accountTokenOwnershipLimit" => {343 limits.account_token_ownership_limit = Some(value);344 }345 "sponsoredDataSize" => {346 limits.sponsored_data_size = Some(value);347 }348 "sponsoredDataRateLimit" => {349 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));350 }351 "tokenLimit" => {352 limits.token_limit = Some(value);353 }354 "sponsorTransferTimeout" => {355 limits.sponsor_transfer_timeout = Some(value);356 }357 "sponsorApproveTimeout" => {358 limits.sponsor_approve_timeout = Some(value);359 }360 "ownerCanTransfer" => {361 limits.owner_can_transfer = Some(convert_value_to_bool()?);362 }363 "ownerCanDestroy" => {364 limits.owner_can_destroy = Some(convert_value_to_bool()?);365 }366 "transfersEnabled" => {367 limits.transfers_enabled = Some(convert_value_to_bool()?);368 }369 _ => return Err(Error::Revert(format!("unknown limit \"{}\"", limit))),370 }371372 let caller = T::CrossAccountId::from_eth(caller);373 <Pallet<T>>::update_limits(&caller, self, limits).map_err(dispatch_to_evm::<T>)374 }375376 /// Get contract address.377 fn contract_address(&self) -> Result<address> {378 Ok(crate::eth::collection_id_to_address(self.id))379 }380381 /// Add collection admin.382 /// @param newAdmin Cross account administrator address.383 fn add_collection_admin_cross(384 &mut self,385 caller: caller,386 new_admin: EthCrossAccount,387 ) -> Result<void> {388 self.consume_store_reads_and_writes(2, 2)?;389390 let caller = T::CrossAccountId::from_eth(caller);391 let new_admin = new_admin.into_sub_cross_account::<T>()?;392 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;393 Ok(())394 }395396 /// Remove collection admin.397 /// @param admin Cross account administrator address.398 fn remove_collection_admin_cross(399 &mut self,400 caller: caller,401 admin: EthCrossAccount,402 ) -> Result<void> {403 self.consume_store_reads_and_writes(2, 2)?;404405 let caller = T::CrossAccountId::from_eth(caller);406 let admin = admin.into_sub_cross_account::<T>()?;407 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;408 Ok(())409 }410411 /// Add collection admin.412 /// @param newAdmin Address of the added administrator.413 #[solidity(hide)]414 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {415 self.consume_store_reads_and_writes(2, 2)?;416417 let caller = T::CrossAccountId::from_eth(caller);418 let new_admin = T::CrossAccountId::from_eth(new_admin);419 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;420 Ok(())421 }422423 /// Remove collection admin.424 ///425 /// @param admin Address of the removed administrator.426 #[solidity(hide)]427 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {428 self.consume_store_reads_and_writes(2, 2)?;429430 let caller = T::CrossAccountId::from_eth(caller);431 let admin = T::CrossAccountId::from_eth(admin);432 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;433 Ok(())434 }435436 /// Toggle accessibility of collection nesting.437 ///438 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'439 #[solidity(rename_selector = "setCollectionNesting")]440 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {441 self.consume_store_reads_and_writes(1, 1)?;442443 let caller = T::CrossAccountId::from_eth(caller);444445 let mut permissions = self.collection.permissions.clone();446 let mut nesting = permissions.nesting().clone();447 nesting.token_owner = enable;448 nesting.restricted = None;449 permissions.nesting = Some(nesting);450451 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)452 }453454 /// Toggle accessibility of collection nesting.455 ///456 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'457 /// @param collections Addresses of collections that will be available for nesting.458 #[solidity(rename_selector = "setCollectionNesting")]459 fn set_nesting(460 &mut self,461 caller: caller,462 enable: bool,463 collections: Vec<address>,464 ) -> Result<void> {465 self.consume_store_reads_and_writes(1, 1)?;466467 if collections.is_empty() {468 return Err("no addresses provided".into());469 }470 let caller = T::CrossAccountId::from_eth(caller);471472 let mut permissions = self.collection.permissions.clone();473 match enable {474 false => {475 let mut nesting = permissions.nesting().clone();476 nesting.token_owner = false;477 nesting.restricted = None;478 permissions.nesting = Some(nesting);479 }480 true => {481 let mut bv = OwnerRestrictedSet::new();482 for i in collections {483 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {484 Error::Revert("Can't convert address into collection id".into())485 })?)486 .map_err(|_| "too many collections")?;487 }488 let mut nesting = permissions.nesting().clone();489 nesting.token_owner = true;490 nesting.restricted = Some(bv);491 permissions.nesting = Some(nesting);492 }493 };494495 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)496 }497498 /// Returns nesting for a collection499 #[solidity(rename_selector = "collectionNestingRestrictedCollectionIds")]500 fn collection_nesting_restricted_ids(&self) -> Result<(bool, Vec<uint256>)> {501 let nesting = self.collection.permissions.nesting();502503 Ok((504 nesting.token_owner,505 nesting506 .restricted507 .clone()508 .map(|b| b.0.into_inner().iter().map(|id| id.0.into()).collect())509 .unwrap_or_default(),510 ))511 }512513 /// Returns permissions for a collection514 fn collection_nesting_permissions(&self) -> Result<Vec<(EvmPermissions, bool)>> {515 let nesting = self.collection.permissions.nesting();516 Ok(vec![517 (EvmPermissions::CollectionAdmin, nesting.collection_admin),518 (EvmPermissions::TokenOwner, nesting.token_owner),519 ])520 }521 /// Set the collection access method.522 /// @param mode Access mode523 /// 0 for Normal524 /// 1 for AllowList525 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {526 self.consume_store_reads_and_writes(1, 1)?;527528 let caller = T::CrossAccountId::from_eth(caller);529 let permissions = CollectionPermissions {530 access: Some(match mode {531 0 => AccessMode::Normal,532 1 => AccessMode::AllowList,533 _ => return Err("not supported access mode".into()),534 }),535 ..Default::default()536 };537 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)538 }539540 /// Checks that user allowed to operate with collection.541 ///542 /// @param user User address to check.543 fn allowlisted_cross(&self, user: EthCrossAccount) -> Result<bool> {544 let user = user.into_sub_cross_account::<T>()?;545 Ok(Pallet::<T>::allowed(self.id, user))546 }547548 /// Add the user to the allowed list.549 ///550 /// @param user Address of a trusted user.551 #[solidity(hide)]552 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {553 self.consume_store_writes(1)?;554555 let caller = T::CrossAccountId::from_eth(caller);556 let user = T::CrossAccountId::from_eth(user);557 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;558 Ok(())559 }560561 /// Add user to allowed list.562 ///563 /// @param user User cross account address.564 fn add_to_collection_allow_list_cross(565 &mut self,566 caller: caller,567 user: EthCrossAccount,568 ) -> Result<void> {569 self.consume_store_writes(1)?;570571 let caller = T::CrossAccountId::from_eth(caller);572 let user = user.into_sub_cross_account::<T>()?;573 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;574 Ok(())575 }576577 /// Remove the user from the allowed list.578 ///579 /// @param user Address of a removed user.580 #[solidity(hide)]581 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {582 self.consume_store_writes(1)?;583584 let caller = T::CrossAccountId::from_eth(caller);585 let user = T::CrossAccountId::from_eth(user);586 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;587 Ok(())588 }589590 /// Remove user from allowed list.591 ///592 /// @param user User cross account address.593 fn remove_from_collection_allow_list_cross(594 &mut self,595 caller: caller,596 user: EthCrossAccount,597 ) -> Result<void> {598 self.consume_store_writes(1)?;599600 let caller = T::CrossAccountId::from_eth(caller);601 let user = user.into_sub_cross_account::<T>()?;602 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;603 Ok(())604 }605606 /// Switch permission for minting.607 ///608 /// @param mode Enable if "true".609 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {610 self.consume_store_reads_and_writes(1, 1)?;611612 let caller = T::CrossAccountId::from_eth(caller);613 let permissions = CollectionPermissions {614 mint_mode: Some(mode),615 ..Default::default()616 };617 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)618 }619620 /// Check that account is the owner or admin of the collection621 ///622 /// @param user account to verify623 /// @return "true" if account is the owner or admin624 #[solidity(hide, rename_selector = "isOwnerOrAdmin")]625 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {626 let user = T::CrossAccountId::from_eth(user);627 Ok(self.is_owner_or_admin(&user))628 }629630 /// Check that account is the owner or admin of the collection631 ///632 /// @param user User cross account to verify633 /// @return "true" if account is the owner or admin634 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {635 let user = user.into_sub_cross_account::<T>()?;636 Ok(self.is_owner_or_admin(&user))637 }638639 /// Returns collection type640 ///641 /// @return `Fungible` or `NFT` or `ReFungible`642 fn unique_collection_type(&self) -> Result<string> {643 let mode = match self.collection.mode {644 CollectionMode::Fungible(_) => "Fungible",645 CollectionMode::NFT => "NFT",646 CollectionMode::ReFungible => "ReFungible",647 };648 Ok(mode.into())649 }650651 /// Get collection owner.652 ///653 /// @return Tuble with sponsor address and his substrate mirror.654 /// If address is canonical then substrate mirror is zero and vice versa.655 fn collection_owner(&self) -> Result<EthCrossAccount> {656 Ok(EthCrossAccount::from_sub_cross_account::<T>(657 &T::CrossAccountId::from_sub(self.owner.clone()),658 ))659 }660661 /// Changes collection owner to another account662 ///663 /// @dev Owner can be changed only by current owner664 /// @param newOwner new owner account665 #[solidity(hide, rename_selector = "changeCollectionOwner")]666 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {667 self.consume_store_writes(1)?;668669 let caller = T::CrossAccountId::from_eth(caller);670 let new_owner = T::CrossAccountId::from_eth(new_owner);671 self.change_owner(caller, new_owner)672 .map_err(dispatch_to_evm::<T>)673 }674675 /// Get collection administrators676 ///677 /// @return Vector of tuples with admins address and his substrate mirror.678 /// If address is canonical then substrate mirror is zero and vice versa.679 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {680 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))681 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))682 .collect();683 Ok(result)684 }685686 /// Changes collection owner to another account687 ///688 /// @dev Owner can be changed only by current owner689 /// @param newOwner new owner cross account690 fn change_collection_owner_cross(691 &mut self,692 caller: caller,693 new_owner: EthCrossAccount,694 ) -> Result<void> {695 self.consume_store_writes(1)?;696697 let caller = T::CrossAccountId::from_eth(caller);698 let new_owner = new_owner.into_sub_cross_account::<T>()?;699 self.change_owner(caller, new_owner)700 .map_err(dispatch_to_evm::<T>)701 }702}703704/// ### Note705/// Do not forget to add: `self.consume_store_reads(1)?;`706fn check_is_owner_or_admin<T: Config>(707 caller: caller,708 collection: &CollectionHandle<T>,709) -> Result<T::CrossAccountId> {710 let caller = T::CrossAccountId::from_eth(caller);711 collection712 .check_is_owner_or_admin(&caller)713 .map_err(dispatch_to_evm::<T>)?;714 Ok(caller)715}716717/// ### Note718/// Do not forget to add: `self.consume_store_writes(1)?;`719fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {720 collection721 .check_is_internal()722 .map_err(dispatch_to_evm::<T>)?;723 collection.save().map_err(dispatch_to_evm::<T>)?;724 Ok(())725}726727/// Contains static property keys and values.728pub mod static_property {729 use evm_coder::{730 execution::{Result, Error},731 };732 use alloc::format;733734 const EXPECT_CONVERT_ERROR: &str = "length < limit";735736 /// Keys.737 pub mod key {738 use super::*;739740 /// Key "baseURI".741 pub fn base_uri() -> up_data_structs::PropertyKey {742 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)743 }744745 /// Key "url".746 pub fn url() -> up_data_structs::PropertyKey {747 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)748 }749750 /// Key "suffix".751 pub fn suffix() -> up_data_structs::PropertyKey {752 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)753 }754755 /// Key "parentNft".756 pub fn parent_nft() -> up_data_structs::PropertyKey {757 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)758 }759 }760761 /// Convert `byte` to [`PropertyKey`].762 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {763 bytes.to_vec().try_into().map_err(|_| {764 Error::Revert(format!(765 "Property key is too long. Max length is {}.",766 up_data_structs::PropertyKey::bound()767 ))768 })769 }770771 /// Convert `bytes` to [`PropertyValue`].772 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {773 bytes.to_vec().try_into().map_err(|_| {774 Error::Revert(format!(775 "Property key is too long. Max length is {}.",776 up_data_structs::PropertyKey::bound()777 ))778 })779 }780}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.1819pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};20use evm_coder::{21 abi::AbiType,22 solidity_interface, solidity, ToLog,23 types::*,24 types::Property as PropertyStruct,25 execution::{Result, Error},26 weight,27};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::{vec, vec::Vec};30use up_data_structs::{31 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,32 SponsoringRateLimit, SponsorshipState,33};34use alloc::format;3536use crate::{37 Pallet, CollectionHandle, Config, CollectionProperties, SelfWeightOf,38 eth::{39 EthCrossAccount, convert_cross_account_to_uint256, CollectionPermissions as EvmPermissions,40 },41 weights::WeightInfo,42};4344/// Events for ethereum collection helper.45#[derive(ToLog)]46pub enum CollectionHelpersEvents {47 /// The collection has been created.48 CollectionCreated {49 /// Collection owner.50 #[indexed]51 owner: address,5253 /// Collection ID.54 #[indexed]55 collection_id: address,56 },57 /// The collection has been destroyed.58 CollectionDestroyed {59 /// Collection ID.60 #[indexed]61 collection_id: address,62 },63 /// The collection has been changed.64 CollectionChanged {65 /// Collection ID.66 #[indexed]67 collection_id: address,68 },6970 /// The token has been changed.71 TokenChanged {72 /// Collection ID.73 #[indexed]74 collection_id: address,75 /// Token ID.76 token_id: uint256,77 },78}7980/// Does not always represent a full collection, for RFT it is either81/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).82pub trait CommonEvmHandler {83 /// Raw compiled binary code of the contract stub84 const CODE: &'static [u8];8586 /// Call precompiled handle.87 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;88}8990/// @title A contract that allows you to work with collections.91#[solidity_interface(name = Collection)]92impl<T: Config> CollectionHandle<T>93where94 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,95{96 /// Set collection property.97 ///98 /// @param key Property key.99 /// @param value Propery value.100 #[solidity(hide)]101 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]102 fn set_collection_property(103 &mut self,104 caller: caller,105 key: string,106 value: bytes,107 ) -> Result<void> {108 let caller = T::CrossAccountId::from_eth(caller);109 let key = <Vec<u8>>::from(key)110 .try_into()111 .map_err(|_| "key too large")?;112 let value = value.0.try_into().map_err(|_| "value too large")?;113114 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })115 .map_err(dispatch_to_evm::<T>)116 }117118 /// Set collection properties.119 ///120 /// @param properties Vector of properties key/value pair.121 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]122 fn set_collection_properties(123 &mut self,124 caller: caller,125 properties: Vec<PropertyStruct>,126 ) -> Result<void> {127 let caller = T::CrossAccountId::from_eth(caller);128129 let properties = properties130 .into_iter()131 .map(|PropertyStruct { key, value }| {132 let key = <Vec<u8>>::from(key)133 .try_into()134 .map_err(|_| "key too large")?;135136 let value = value.0.try_into().map_err(|_| "value too large")?;137138 Ok(Property { key, value })139 })140 .collect::<Result<Vec<_>>>()?;141142 <Pallet<T>>::set_collection_properties(self, &caller, properties)143 .map_err(dispatch_to_evm::<T>)144 }145146 /// Delete collection property.147 ///148 /// @param key Property key.149 #[solidity(hide)]150 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]151 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {152 let caller = T::CrossAccountId::from_eth(caller);153 let key = <Vec<u8>>::from(key)154 .try_into()155 .map_err(|_| "key too large")?;156157 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)158 }159160 /// Delete collection properties.161 ///162 /// @param keys Properties keys.163 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]164 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {165 let caller = T::CrossAccountId::from_eth(caller);166 let keys = keys167 .into_iter()168 .map(|key| {169 <Vec<u8>>::from(key)170 .try_into()171 .map_err(|_| Error::Revert("key too large".into()))172 })173 .collect::<Result<Vec<_>>>()?;174175 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)176 }177178 /// Get collection property.179 ///180 /// @dev Throws error if key not found.181 ///182 /// @param key Property key.183 /// @return bytes The property corresponding to the key.184 fn collection_property(&self, key: string) -> Result<bytes> {185 let key = <Vec<u8>>::from(key)186 .try_into()187 .map_err(|_| "key too large")?;188189 let props = CollectionProperties::<T>::get(self.id);190 let prop = props.get(&key).ok_or("key not found")?;191192 Ok(bytes(prop.to_vec()))193 }194195 /// Get collection properties.196 ///197 /// @param keys Properties keys. Empty keys for all propertyes.198 /// @return Vector of properties key/value pairs.199 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<PropertyStruct>> {200 let keys = keys201 .into_iter()202 .map(|key| {203 <Vec<u8>>::from(key)204 .try_into()205 .map_err(|_| Error::Revert("key too large".into()))206 })207 .collect::<Result<Vec<_>>>()?;208209 let properties = Pallet::<T>::filter_collection_properties(210 self.id,211 if keys.is_empty() { None } else { Some(keys) },212 )213 .map_err(dispatch_to_evm::<T>)?;214215 let properties = properties216 .into_iter()217 .map(|p| {218 let key =219 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;220 let value = bytes(p.value.to_vec());221 Ok(PropertyStruct { key, value })222 })223 .collect::<Result<Vec<_>>>()?;224 Ok(properties)225 }226227 /// Set the sponsor of the collection.228 ///229 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.230 ///231 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.232 #[solidity(hide)]233 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {234 self.consume_store_reads_and_writes(1, 1)?;235236 let caller = T::CrossAccountId::from_eth(caller);237238 let sponsor = T::CrossAccountId::from_eth(sponsor);239 self.set_sponsor(&caller, sponsor.as_sub().clone())240 .map_err(dispatch_to_evm::<T>)241 }242243 /// Set the sponsor of the collection.244 ///245 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.246 ///247 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.248 fn set_collection_sponsor_cross(249 &mut self,250 caller: caller,251 sponsor: EthCrossAccount,252 ) -> Result<void> {253 self.consume_store_reads_and_writes(1, 1)?;254255 let caller = T::CrossAccountId::from_eth(caller);256257 let sponsor = sponsor.into_sub_cross_account::<T>()?;258 self.set_sponsor(&caller, sponsor.as_sub().clone())259 .map_err(dispatch_to_evm::<T>)260 }261262 /// Whether there is a pending sponsor.263 fn has_collection_pending_sponsor(&self) -> Result<bool> {264 Ok(matches!(265 self.collection.sponsorship,266 SponsorshipState::Unconfirmed(_)267 ))268 }269270 /// Collection sponsorship confirmation.271 ///272 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.273 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {274 self.consume_store_writes(1)?;275276 let caller = T::CrossAccountId::from_eth(caller);277 self.confirm_sponsorship(caller.as_sub())278 .map_err(dispatch_to_evm::<T>)279 }280281 /// Remove collection sponsor.282 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {283 self.consume_store_reads_and_writes(1, 1)?;284 let caller = T::CrossAccountId::from_eth(caller);285 self.remove_sponsor(&caller).map_err(dispatch_to_evm::<T>)286 }287288 /// Get current sponsor.289 ///290 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.291 fn collection_sponsor(&self) -> Result<(address, uint256)> {292 let sponsor = match self.collection.sponsorship.sponsor() {293 Some(sponsor) => sponsor,294 None => return Ok(Default::default()),295 };296 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());297 let result: (address, uint256) = if sponsor.is_canonical_substrate() {298 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);299 (Default::default(), sponsor)300 } else {301 let sponsor = *sponsor.as_eth();302 (sponsor, Default::default())303 };304 Ok(result)305 }306307 /// Set limits for the collection.308 /// @dev Throws error if limit not found.309 /// @param limit Name of the limit. Valid names:310 /// "accountTokenOwnershipLimit",311 /// "sponsoredDataSize",312 /// "sponsoredDataRateLimit",313 /// "tokenLimit",314 /// "sponsorTransferTimeout",315 /// "sponsorApproveTimeout"316 /// "ownerCanTransfer",317 /// "ownerCanDestroy",318 /// "transfersEnabled"319 /// @param value Value of the limit.320 #[solidity(rename_selector = "setCollectionLimit")]321 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint256) -> Result<void> {322 self.consume_store_reads_and_writes(1, 1)?;323324 let value = value325 .try_into()326 .map_err(|_| Error::Revert(format!("can't convert value to u32 \"{}\"", value)))?;327328 let convert_value_to_bool = || match value {329 0 => Ok(false),330 1 => Ok(true),331 _ => {332 return Err(Error::Revert(format!(333 "can't convert value to boolean \"{}\"",334 value335 )))336 }337 };338339 let mut limits = self.limits.clone();340341 match limit.as_str() {342 "accountTokenOwnershipLimit" => {343 limits.account_token_ownership_limit = Some(value);344 }345 "sponsoredDataSize" => {346 limits.sponsored_data_size = Some(value);347 }348 "sponsoredDataRateLimit" => {349 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));350 }351 "tokenLimit" => {352 limits.token_limit = Some(value);353 }354 "sponsorTransferTimeout" => {355 limits.sponsor_transfer_timeout = Some(value);356 }357 "sponsorApproveTimeout" => {358 limits.sponsor_approve_timeout = Some(value);359 }360 "ownerCanTransfer" => {361 limits.owner_can_transfer = Some(convert_value_to_bool()?);362 }363 "ownerCanDestroy" => {364 limits.owner_can_destroy = Some(convert_value_to_bool()?);365 }366 "transfersEnabled" => {367 limits.transfers_enabled = Some(convert_value_to_bool()?);368 }369 _ => return Err(Error::Revert(format!("unknown limit \"{}\"", limit))),370 }371372 let caller = T::CrossAccountId::from_eth(caller);373 <Pallet<T>>::update_limits(&caller, self, limits).map_err(dispatch_to_evm::<T>)374 }375376 /// Get contract address.377 fn contract_address(&self) -> Result<address> {378 Ok(crate::eth::collection_id_to_address(self.id))379 }380381 /// Add collection admin.382 /// @param newAdmin Cross account administrator address.383 fn add_collection_admin_cross(384 &mut self,385 caller: caller,386 new_admin: EthCrossAccount,387 ) -> Result<void> {388 self.consume_store_reads_and_writes(2, 2)?;389390 let caller = T::CrossAccountId::from_eth(caller);391 let new_admin = new_admin.into_sub_cross_account::<T>()?;392 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;393 Ok(())394 }395396 /// Remove collection admin.397 /// @param admin Cross account administrator address.398 fn remove_collection_admin_cross(399 &mut self,400 caller: caller,401 admin: EthCrossAccount,402 ) -> Result<void> {403 self.consume_store_reads_and_writes(2, 2)?;404405 let caller = T::CrossAccountId::from_eth(caller);406 let admin = admin.into_sub_cross_account::<T>()?;407 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;408 Ok(())409 }410411 /// Add collection admin.412 /// @param newAdmin Address of the added administrator.413 #[solidity(hide)]414 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {415 self.consume_store_reads_and_writes(2, 2)?;416417 let caller = T::CrossAccountId::from_eth(caller);418 let new_admin = T::CrossAccountId::from_eth(new_admin);419 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;420 Ok(())421 }422423 /// Remove collection admin.424 ///425 /// @param admin Address of the removed administrator.426 #[solidity(hide)]427 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {428 self.consume_store_reads_and_writes(2, 2)?;429430 let caller = T::CrossAccountId::from_eth(caller);431 let admin = T::CrossAccountId::from_eth(admin);432 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;433 Ok(())434 }435436 /// Toggle accessibility of collection nesting.437 ///438 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'439 #[solidity(rename_selector = "setCollectionNesting")]440 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {441 self.consume_store_reads_and_writes(1, 1)?;442443 let caller = T::CrossAccountId::from_eth(caller);444445 let mut permissions = self.collection.permissions.clone();446 let mut nesting = permissions.nesting().clone();447 nesting.token_owner = enable;448 nesting.restricted = None;449 permissions.nesting = Some(nesting);450451 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)452 }453454 /// Toggle accessibility of collection nesting.455 ///456 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'457 /// @param collections Addresses of collections that will be available for nesting.458 #[solidity(rename_selector = "setCollectionNesting")]459 fn set_nesting(460 &mut self,461 caller: caller,462 enable: bool,463 collections: Vec<address>,464 ) -> Result<void> {465 self.consume_store_reads_and_writes(1, 1)?;466467 if collections.is_empty() {468 return Err("no addresses provided".into());469 }470 let caller = T::CrossAccountId::from_eth(caller);471472 let mut permissions = self.collection.permissions.clone();473 match enable {474 false => {475 let mut nesting = permissions.nesting().clone();476 nesting.token_owner = false;477 nesting.restricted = None;478 permissions.nesting = Some(nesting);479 }480 true => {481 let mut bv = OwnerRestrictedSet::new();482 for i in collections {483 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {484 Error::Revert("Can't convert address into collection id".into())485 })?)486 .map_err(|_| "too many collections")?;487 }488 let mut nesting = permissions.nesting().clone();489 nesting.token_owner = true;490 nesting.restricted = Some(bv);491 permissions.nesting = Some(nesting);492 }493 };494495 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)496 }497498 /// Returns nesting for a collection499 #[solidity(rename_selector = "collectionNestingRestrictedCollectionIds")]500 fn collection_nesting_restricted_ids(&self) -> Result<(bool, Vec<uint256>)> {501 let nesting = self.collection.permissions.nesting();502503 Ok((504 nesting.token_owner,505 nesting506 .restricted507 .clone()508 .map(|b| b.0.into_inner().iter().map(|id| id.0.into()).collect())509 .unwrap_or_default(),510 ))511 }512513 /// Returns permissions for a collection514 fn collection_nesting_permissions(&self) -> Result<Vec<(EvmPermissions, bool)>> {515 let nesting = self.collection.permissions.nesting();516 Ok(vec![517 (EvmPermissions::CollectionAdmin, nesting.collection_admin),518 (EvmPermissions::TokenOwner, nesting.token_owner),519 ])520 }521 /// Set the collection access method.522 /// @param mode Access mode523 /// 0 for Normal524 /// 1 for AllowList525 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {526 self.consume_store_reads_and_writes(1, 1)?;527528 let caller = T::CrossAccountId::from_eth(caller);529 let permissions = CollectionPermissions {530 access: Some(match mode {531 0 => AccessMode::Normal,532 1 => AccessMode::AllowList,533 _ => return Err("not supported access mode".into()),534 }),535 ..Default::default()536 };537 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)538 }539540 /// Checks that user allowed to operate with collection.541 ///542 /// @param user User address to check.543 fn allowlisted_cross(&self, user: EthCrossAccount) -> Result<bool> {544 let user = user.into_sub_cross_account::<T>()?;545 Ok(Pallet::<T>::allowed(self.id, user))546 }547548 /// Add the user to the allowed list.549 ///550 /// @param user Address of a trusted user.551 #[solidity(hide)]552 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {553 self.consume_store_writes(1)?;554555 let caller = T::CrossAccountId::from_eth(caller);556 let user = T::CrossAccountId::from_eth(user);557 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;558 Ok(())559 }560561 /// Add user to allowed list.562 ///563 /// @param user User cross account address.564 fn add_to_collection_allow_list_cross(565 &mut self,566 caller: caller,567 user: EthCrossAccount,568 ) -> Result<void> {569 self.consume_store_writes(1)?;570571 let caller = T::CrossAccountId::from_eth(caller);572 let user = user.into_sub_cross_account::<T>()?;573 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;574 Ok(())575 }576577 /// Remove the user from the allowed list.578 ///579 /// @param user Address of a removed user.580 #[solidity(hide)]581 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {582 self.consume_store_writes(1)?;583584 let caller = T::CrossAccountId::from_eth(caller);585 let user = T::CrossAccountId::from_eth(user);586 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;587 Ok(())588 }589590 /// Remove user from allowed list.591 ///592 /// @param user User cross account address.593 fn remove_from_collection_allow_list_cross(594 &mut self,595 caller: caller,596 user: EthCrossAccount,597 ) -> Result<void> {598 self.consume_store_writes(1)?;599600 let caller = T::CrossAccountId::from_eth(caller);601 let user = user.into_sub_cross_account::<T>()?;602 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;603 Ok(())604 }605606 /// Switch permission for minting.607 ///608 /// @param mode Enable if "true".609 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {610 self.consume_store_reads_and_writes(1, 1)?;611612 let caller = T::CrossAccountId::from_eth(caller);613 let permissions = CollectionPermissions {614 mint_mode: Some(mode),615 ..Default::default()616 };617 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)618 }619620 /// Check that account is the owner or admin of the collection621 ///622 /// @param user account to verify623 /// @return "true" if account is the owner or admin624 #[solidity(hide, rename_selector = "isOwnerOrAdmin")]625 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {626 let user = T::CrossAccountId::from_eth(user);627 Ok(self.is_owner_or_admin(&user))628 }629630 /// Check that account is the owner or admin of the collection631 ///632 /// @param user User cross account to verify633 /// @return "true" if account is the owner or admin634 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {635 let user = user.into_sub_cross_account::<T>()?;636 Ok(self.is_owner_or_admin(&user))637 }638639 /// Returns collection type640 ///641 /// @return `Fungible` or `NFT` or `ReFungible`642 fn unique_collection_type(&self) -> Result<string> {643 let mode = match self.collection.mode {644 CollectionMode::Fungible(_) => "Fungible",645 CollectionMode::NFT => "NFT",646 CollectionMode::ReFungible => "ReFungible",647 };648 Ok(mode.into())649 }650651 /// Get collection owner.652 ///653 /// @return Tuble with sponsor address and his substrate mirror.654 /// If address is canonical then substrate mirror is zero and vice versa.655 fn collection_owner(&self) -> Result<EthCrossAccount> {656 Ok(EthCrossAccount::from_sub_cross_account::<T>(657 &T::CrossAccountId::from_sub(self.owner.clone()),658 ))659 }660661 /// Changes collection owner to another account662 ///663 /// @dev Owner can be changed only by current owner664 /// @param newOwner new owner account665 #[solidity(hide, rename_selector = "changeCollectionOwner")]666 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {667 self.consume_store_writes(1)?;668669 let caller = T::CrossAccountId::from_eth(caller);670 let new_owner = T::CrossAccountId::from_eth(new_owner);671 self.change_owner(caller, new_owner)672 .map_err(dispatch_to_evm::<T>)673 }674675 /// Get collection administrators676 ///677 /// @return Vector of tuples with admins address and his substrate mirror.678 /// If address is canonical then substrate mirror is zero and vice versa.679 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {680 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))681 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))682 .collect();683 Ok(result)684 }685686 /// Changes collection owner to another account687 ///688 /// @dev Owner can be changed only by current owner689 /// @param newOwner new owner cross account690 fn change_collection_owner_cross(691 &mut self,692 caller: caller,693 new_owner: EthCrossAccount,694 ) -> Result<void> {695 self.consume_store_writes(1)?;696697 let caller = T::CrossAccountId::from_eth(caller);698 let new_owner = new_owner.into_sub_cross_account::<T>()?;699 self.change_owner(caller, new_owner)700 .map_err(dispatch_to_evm::<T>)701 }702}703704/// Contains static property keys and values.705pub mod static_property {706 use evm_coder::{707 execution::{Result, Error},708 };709 use alloc::format;710711 const EXPECT_CONVERT_ERROR: &str = "length < limit";712713 /// Keys.714 pub mod key {715 use super::*;716717 /// Key "baseURI".718 pub fn base_uri() -> up_data_structs::PropertyKey {719 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)720 }721722 /// Key "url".723 pub fn url() -> up_data_structs::PropertyKey {724 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)725 }726727 /// Key "suffix".728 pub fn suffix() -> up_data_structs::PropertyKey {729 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)730 }731732 /// Key "parentNft".733 pub fn parent_nft() -> up_data_structs::PropertyKey {734 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)735 }736 }737738 /// Convert `byte` to [`PropertyKey`].739 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {740 bytes.to_vec().try_into().map_err(|_| {741 Error::Revert(format!(742 "Property key is too long. Max length is {}.",743 up_data_structs::PropertyKey::bound()744 ))745 })746 }747748 /// Convert `bytes` to [`PropertyValue`].749 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {750 bytes.to_vec().try_into().map_err(|_| {751 Error::Revert(format!(752 "Property key is too long. Max length is {}.",753 up_data_structs::PropertyKey::bound()754 ))755 })756 }757}pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -121,6 +121,7 @@
}
impl EthCrossAccount {
+ /// Converts `CrossAccountId` to `EthCrossAccount` to be correctly usable with Ethereum.
pub fn from_sub_cross_account<T>(cross_account_id: &T::CrossAccountId) -> Self
where
T: pallet_evm::Config,
@@ -139,6 +140,7 @@
}
}
+ /// Converts `EthCrossAccount` to `CrossAccountId` to be correctly usable with Substrate.
pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>
where
T: pallet_evm::Config,
@@ -155,10 +157,14 @@
}
}
}
+
+/// Descriptor of the kind of user to be used within collection permissions on certain operations.
#[derive(Default, Debug, Clone, Copy, AbiCoder)]
#[repr(u8)]
pub enum CollectionPermissions {
+ /// Collection admin.
#[default]
CollectionAdmin,
+ /// Owner of a token.
TokenOwner,
}
pallets/foreign-assets/Cargo.tomldiffbeforeafterboth--- a/pallets/foreign-assets/Cargo.toml
+++ b/pallets/foreign-assets/Cargo.toml
@@ -1,5 +1,3 @@
-cargo-features = ["workspace-inheritance"]
-
[package]
name = "pallet-foreign-assets"
version = "0.1.0"
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -90,12 +90,11 @@
use crate::erc_token::ERC20Events;
use crate::erc::ERC721Events;
-use codec::{Encode, Decode, MaxEncodedLen};
use core::ops::Deref;
use derivative::Derivative;
use evm_coder::ToLog;
use frame_support::{
- BoundedBTreeMap, BoundedVec, ensure, fail, storage::with_transaction, transactional,
+ BoundedBTreeMap, ensure, fail, storage::with_transaction, transactional,
pallet_prelude::ConstU32,
};
use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm};
@@ -105,15 +104,14 @@
Event as CommonEvent, Pallet as PalletCommon, erc::CollectionHelpersEvents,
};
use pallet_structure::Pallet as PalletStructure;
-use scale_info::TypeInfo;
use sp_core::{Get, H160};
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
use up_data_structs::{
AccessMode, budget::Budget, CollectionId, CollectionFlags, CollectionPropertiesVec,
- CreateCollectionData, CustomDataLimit, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH,
- MAX_REFUNGIBLE_PIECES, Property, PropertyKey, PropertyKeyPermission, PropertyPermission,
- PropertyScope, PropertyValue, TokenId, TrySetProperty,
+ CreateCollectionData, mapping::TokenAddressMapping, MAX_ITEMS_PER_BATCH, MAX_REFUNGIBLE_PIECES,
+ Property, PropertyKey, PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue,
+ TokenId, TrySetProperty,
};
pub use pallet::*;
@@ -133,17 +131,6 @@
}
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
-/// Token data, stored independently from other data used to describe it
-/// for the convenience of database access. Notably contains the token metadata.
-#[struct_versioning::versioned(version = 2, upper)]
-#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
-pub struct ItemData {
- pub const_data: BoundedVec<u8, CustomDataLimit>,
-
- #[version(..2)]
- pub variable_data: BoundedVec<u8, CustomDataLimit>,
-}
-
#[frame_support::pallet]
pub mod pallet {
use super::*;
@@ -151,7 +138,6 @@
Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key,
traits::StorageVersion,
};
- use frame_system::pallet_prelude::*;
use up_data_structs::{CollectionId, TokenId};
use super::weights::WeightInfo;
@@ -192,16 +178,6 @@
#[pallet::storage]
pub type TokensBurnt<T: Config> =
StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;
-
- /// Token data, used to partially describe a token.
- // TODO: remove
- #[pallet::storage]
- #[deprecated(since = "0.2.0", note = "ItemData is no more contains usefull data")]
- pub type TokenData<T: Config> = StorageNMap<
- Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),
- Value = ItemData,
- QueryKind = ValueQuery,
- >;
/// Amount of pieces a refungible token is split into.
#[pallet::storage]
@@ -284,20 +260,6 @@
Value = bool,
QueryKind = ValueQuery,
>;
-
- #[pallet::hooks]
- impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
- fn on_runtime_upgrade() -> Weight {
- let storage_version = StorageVersion::get::<Pallet<T>>();
- if storage_version < StorageVersion::new(2) {
- #[allow(deprecated)]
- let _ = <TokenData<T>>::clear(u32::MAX, None);
- }
- StorageVersion::new(2).put::<Pallet<T>>();
-
- Weight::zero()
- }
- }
}
pub struct RefungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -74,7 +74,7 @@
extern crate alloc;
use frame_support::{
- decl_module, decl_storage, decl_error, decl_event,
+ decl_module, decl_storage, decl_error,
dispatch::DispatchResult,
ensure, fail,
weights::{Weight},
@@ -91,7 +91,7 @@
CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,
CreateCollectionData, CreateItemExData, budget, Property, PropertyKey, PropertyKeyPermission,
};
-use pallet_evm::{account::CrossAccountId};
+use pallet_evm::account::CrossAccountId;
use pallet_common::{
CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,
dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,
runtime/common/config/pallets/app_promotion.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/app_promotion.rs
+++ b/runtime/common/config/pallets/app_promotion.rs
@@ -22,7 +22,7 @@
use frame_support::{parameter_types, PalletId};
use sp_arithmetic::Perbill;
use up_common::{
- constants::{UNIQUE, RELAY_DAYS, DAYS},
+ constants::{UNIQUE, RELAY_DAYS},
types::Balance,
};
runtime/opal/Cargo.tomldiffbeforeafterboth--- a/runtime/opal/Cargo.toml
+++ b/runtime/opal/Cargo.toml
@@ -1,8 +1,6 @@
################################################################################
# Package
-cargo-features = ["workspace-inheritance"]
-
[package]
authors = ['Unique Network <support@uniquenetwork.io>']
build = 'build.rs'
runtime/quartz/Cargo.tomldiffbeforeafterboth--- a/runtime/quartz/Cargo.toml
+++ b/runtime/quartz/Cargo.toml
@@ -1,8 +1,6 @@
################################################################################
# Package
-cargo-features = ["workspace-inheritance"]
-
[package]
authors = ['Unique Network <support@uniquenetwork.io>']
build = 'build.rs'
runtime/unique/Cargo.tomldiffbeforeafterboth--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -1,8 +1,6 @@
################################################################################
# Package
-cargo-features = ["workspace-inheritance"]
-
[package]
authors = ['Unique Network <support@uniquenetwork.io>']
build = 'build.rs'