difftreelog
Merge branch 'develop' into feature/ci-refactoring
in: master
6 files changed
crates/evm-coder/procedural/src/solidity_interface.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/solidity_interface.rs
+++ b/crates/evm-coder/procedural/src/solidity_interface.rs
@@ -718,7 +718,11 @@
}
}
} else {
- quote! {#pascal_name}
+ quote! {
+ #(#[doc = #docs])*
+ #[allow(missing_docs)]
+ #pascal_name
+ }
}
}
@@ -788,6 +792,7 @@
quote! {
#call_name::#pascal_name #matcher => {
+ #[allow(deprecated)]
let result = #receiver #name(
#(
#args,
crates/evm-coder/procedural/src/to_log.rsdiffbeforeafterboth--- a/crates/evm-coder/procedural/src/to_log.rs
+++ b/crates/evm-coder/procedural/src/to_log.rs
@@ -194,7 +194,7 @@
#(
#consts
)*
-
+ /// Generate solidity definitions for methods described in this interface
pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {
use evm_coder::solidity::*;
use core::fmt::Write;
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 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_cross_account_to_tuple,38 convert_tuple_to_cross_account,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 /// The collection has been destroyed.57 CollectionDestroyed {58 /// Collection ID.59 #[indexed]60 collection_id: address,61 },62}6364/// Does not always represent a full collection, for RFT it is either65/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).66pub trait CommonEvmHandler {67 const CODE: &'static [u8];6869 /// Call precompiled handle.70 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;71}7273/// @title A contract that allows you to work with collections.74#[solidity_interface(name = Collection)]75impl<T: Config> CollectionHandle<T>76where77 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,78{79 /// Set collection property.80 ///81 /// @param key Property key.82 /// @param value Propery value.83 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]84 fn set_collection_property(85 &mut self,86 caller: caller,87 key: string,88 value: bytes,89 ) -> Result<void> {90 let caller = T::CrossAccountId::from_eth(caller);91 let key = <Vec<u8>>::from(key)92 .try_into()93 .map_err(|_| "key too large")?;94 let value = value.0.try_into().map_err(|_| "value too large")?;9596 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })97 .map_err(dispatch_to_evm::<T>)98 }99100 /// Set collection properties.101 ///102 /// @param properties Vector of properties key/value pair.103 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]104 fn set_collection_properties(105 &mut self,106 caller: caller,107 properties: Vec<(string, bytes)>,108 ) -> Result<void> {109 let caller = T::CrossAccountId::from_eth(caller);110111 let properties = properties112 .into_iter()113 .map(|(key, value)| {114 let key = <Vec<u8>>::from(key)115 .try_into()116 .map_err(|_| "key too large")?;117118 let value = value.0.try_into().map_err(|_| "value too large")?;119120 Ok(Property { key, value })121 })122 .collect::<Result<Vec<_>>>()?;123124 <Pallet<T>>::set_collection_properties(self, &caller, properties)125 .map_err(dispatch_to_evm::<T>)126 }127128 /// Delete collection property.129 ///130 /// @param key Property key.131 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]132 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {133 let caller = T::CrossAccountId::from_eth(caller);134 let key = <Vec<u8>>::from(key)135 .try_into()136 .map_err(|_| "key too large")?;137138 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)139 }140141 /// Delete collection properties.142 ///143 /// @param keys Properties keys.144 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]145 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {146 let caller = T::CrossAccountId::from_eth(caller);147 let keys = keys148 .into_iter()149 .map(|key| {150 <Vec<u8>>::from(key)151 .try_into()152 .map_err(|_| Error::Revert("key too large".into()))153 })154 .collect::<Result<Vec<_>>>()?;155156 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)157 }158159 /// Get collection property.160 ///161 /// @dev Throws error if key not found.162 ///163 /// @param key Property key.164 /// @return bytes The property corresponding to the key.165 fn collection_property(&self, key: string) -> Result<bytes> {166 let key = <Vec<u8>>::from(key)167 .try_into()168 .map_err(|_| "key too large")?;169170 let props = CollectionProperties::<T>::get(self.id);171 let prop = props.get(&key).ok_or("key not found")?;172173 Ok(bytes(prop.to_vec()))174 }175176 /// Get collection properties.177 ///178 /// @param keys Properties keys. Empty keys for all propertyes.179 /// @return Vector of properties key/value pairs.180 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {181 let keys = keys182 .into_iter()183 .map(|key| {184 <Vec<u8>>::from(key)185 .try_into()186 .map_err(|_| Error::Revert("key too large".into()))187 })188 .collect::<Result<Vec<_>>>()?;189190 let properties = Pallet::<T>::filter_collection_properties(191 self.id,192 if keys.is_empty() { None } else { Some(keys) },193 )194 .map_err(dispatch_to_evm::<T>)?;195196 let properties = properties197 .into_iter()198 .map(|p| {199 let key =200 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;201 let value = bytes(p.value.to_vec());202 Ok((key, value))203 })204 .collect::<Result<Vec<_>>>()?;205 Ok(properties)206 }207208 /// Set the sponsor of the collection.209 ///210 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.211 ///212 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.213 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {214 self.consume_store_reads_and_writes(1, 1)?;215216 check_is_owner_or_admin(caller, self)?;217218 let sponsor = T::CrossAccountId::from_eth(sponsor);219 self.set_sponsor(sponsor.as_sub().clone())220 .map_err(dispatch_to_evm::<T>)?;221 save(self)222 }223224 /// Set the sponsor of the collection.225 ///226 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.227 ///228 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.229 fn set_collection_sponsor_cross(230 &mut self,231 caller: caller,232 sponsor: (address, uint256),233 ) -> Result<void> {234 self.consume_store_reads_and_writes(1, 1)?;235236 check_is_owner_or_admin(caller, self)?;237238 let sponsor = convert_tuple_to_cross_account::<T>(sponsor)?;239 self.set_sponsor(sponsor.as_sub().clone())240 .map_err(dispatch_to_evm::<T>)?;241 save(self)242 }243244 /// Whether there is a pending sponsor.245 fn has_collection_pending_sponsor(&self) -> Result<bool> {246 Ok(matches!(247 self.collection.sponsorship,248 SponsorshipState::Unconfirmed(_)249 ))250 }251252 /// Collection sponsorship confirmation.253 ///254 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.255 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {256 self.consume_store_writes(1)?;257258 let caller = T::CrossAccountId::from_eth(caller);259 if !self260 .confirm_sponsorship(caller.as_sub())261 .map_err(dispatch_to_evm::<T>)?262 {263 return Err("caller is not set as sponsor".into());264 }265 save(self)266 }267268 /// Remove collection sponsor.269 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {270 self.consume_store_reads_and_writes(1, 1)?;271 check_is_owner_or_admin(caller, self)?;272 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;273 save(self)274 }275276 /// Get current sponsor.277 ///278 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.279 fn collection_sponsor(&self) -> Result<(address, uint256)> {280 let sponsor = match self.collection.sponsorship.sponsor() {281 Some(sponsor) => sponsor,282 None => return Ok(Default::default()),283 };284 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());285 let result: (address, uint256) = if sponsor.is_canonical_substrate() {286 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);287 (Default::default(), sponsor)288 } else {289 let sponsor = *sponsor.as_eth();290 (sponsor, Default::default())291 };292 Ok(result)293 }294295 /// Set limits for the collection.296 /// @dev Throws error if limit not found.297 /// @param limit Name of the limit. Valid names:298 /// "accountTokenOwnershipLimit",299 /// "sponsoredDataSize",300 /// "sponsoredDataRateLimit",301 /// "tokenLimit",302 /// "sponsorTransferTimeout",303 /// "sponsorApproveTimeout"304 /// @param value Value of the limit.305 #[solidity(rename_selector = "setCollectionLimit")]306 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {307 self.consume_store_reads_and_writes(1, 1)?;308309 check_is_owner_or_admin(caller, self)?;310 let mut limits = self.limits.clone();311312 match limit.as_str() {313 "accountTokenOwnershipLimit" => {314 limits.account_token_ownership_limit = Some(value);315 }316 "sponsoredDataSize" => {317 limits.sponsored_data_size = Some(value);318 }319 "sponsoredDataRateLimit" => {320 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));321 }322 "tokenLimit" => {323 limits.token_limit = Some(value);324 }325 "sponsorTransferTimeout" => {326 limits.sponsor_transfer_timeout = Some(value);327 }328 "sponsorApproveTimeout" => {329 limits.sponsor_approve_timeout = Some(value);330 }331 _ => {332 return Err(Error::Revert(format!(333 "unknown integer limit \"{}\"",334 limit335 )))336 }337 }338 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)339 .map_err(dispatch_to_evm::<T>)?;340 save(self)341 }342343 /// Set limits for the collection.344 /// @dev Throws error if limit not found.345 /// @param limit Name of the limit. Valid names:346 /// "ownerCanTransfer",347 /// "ownerCanDestroy",348 /// "transfersEnabled"349 /// @param value Value of the limit.350 #[solidity(rename_selector = "setCollectionLimit")]351 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {352 self.consume_store_reads_and_writes(1, 1)?;353354 check_is_owner_or_admin(caller, self)?;355 let mut limits = self.limits.clone();356357 match limit.as_str() {358 "ownerCanTransfer" => {359 limits.owner_can_transfer = Some(value);360 }361 "ownerCanDestroy" => {362 limits.owner_can_destroy = Some(value);363 }364 "transfersEnabled" => {365 limits.transfers_enabled = Some(value);366 }367 _ => {368 return Err(Error::Revert(format!(369 "unknown boolean limit \"{}\"",370 limit371 )))372 }373 }374 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)375 .map_err(dispatch_to_evm::<T>)?;376 save(self)377 }378379 /// Get contract address.380 fn contract_address(&self) -> Result<address> {381 Ok(crate::eth::collection_id_to_address(self.id))382 }383384 /// Add collection admin.385 /// @param newAdmin Cross account administrator address.386 fn add_collection_admin_cross(387 &mut self,388 caller: caller,389 new_admin: (address, uint256),390 ) -> Result<void> {391 self.consume_store_writes(2)?;392393 let caller = T::CrossAccountId::from_eth(caller);394 let new_admin = convert_tuple_to_cross_account::<T>(new_admin)?;395 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;396 Ok(())397 }398399 /// Remove collection admin.400 /// @param admin Cross account administrator address.401 fn remove_collection_admin_cross(402 &mut self,403 caller: caller,404 admin: (address, uint256),405 ) -> Result<void> {406 self.consume_store_writes(2)?;407408 let caller = T::CrossAccountId::from_eth(caller);409 let admin = convert_tuple_to_cross_account::<T>(admin)?;410 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;411 Ok(())412 }413414 /// Add collection admin.415 /// @param newAdmin Address of the added administrator.416 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {417 self.consume_store_writes(2)?;418419 let caller = T::CrossAccountId::from_eth(caller);420 let new_admin = T::CrossAccountId::from_eth(new_admin);421 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;422 Ok(())423 }424425 /// Remove collection admin.426 ///427 /// @param admin Address of the removed administrator.428 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {429 self.consume_store_writes(2)?;430431 let caller = T::CrossAccountId::from_eth(caller);432 let admin = T::CrossAccountId::from_eth(admin);433 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;434 Ok(())435 }436437 /// Toggle accessibility of collection nesting.438 ///439 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'440 #[solidity(rename_selector = "setCollectionNesting")]441 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {442 self.consume_store_reads_and_writes(1, 1)?;443444 check_is_owner_or_admin(caller, self)?;445446 let mut permissions = self.collection.permissions.clone();447 let mut nesting = permissions.nesting().clone();448 nesting.token_owner = enable;449 nesting.restricted = None;450 permissions.nesting = Some(nesting);451452 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 /// Toggle accessibility of collection nesting.463 ///464 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'465 /// @param collections Addresses of collections that will be available for nesting.466 #[solidity(rename_selector = "setCollectionNesting")]467 fn set_nesting(468 &mut self,469 caller: caller,470 enable: bool,471 collections: Vec<address>,472 ) -> Result<void> {473 self.consume_store_reads_and_writes(1, 1)?;474475 if collections.is_empty() {476 return Err("no addresses provided".into());477 }478 check_is_owner_or_admin(caller, self)?;479480 let mut permissions = self.collection.permissions.clone();481 match enable {482 false => {483 let mut nesting = permissions.nesting().clone();484 nesting.token_owner = false;485 nesting.restricted = None;486 permissions.nesting = Some(nesting);487 }488 true => {489 let mut bv = OwnerRestrictedSet::new();490 for i in collections {491 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {492 Error::Revert("Can't convert address into collection id".into())493 })?)494 .map_err(|_| "too many collections")?;495 }496 let mut nesting = permissions.nesting().clone();497 nesting.token_owner = true;498 nesting.restricted = Some(bv);499 permissions.nesting = Some(nesting);500 }501 };502503 self.collection.permissions = <Pallet<T>>::clamp_permissions(504 self.collection.mode.clone(),505 &self.collection.permissions,506 permissions,507 )508 .map_err(dispatch_to_evm::<T>)?;509510 save(self)511 }512513 /// Set the collection access method.514 /// @param mode Access mode515 /// 0 for Normal516 /// 1 for AllowList517 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {518 self.consume_store_reads_and_writes(1, 1)?;519520 check_is_owner_or_admin(caller, self)?;521 let permissions = CollectionPermissions {522 access: Some(match mode {523 0 => AccessMode::Normal,524 1 => AccessMode::AllowList,525 _ => return Err("not supported access mode".into()),526 }),527 ..Default::default()528 };529 self.collection.permissions = <Pallet<T>>::clamp_permissions(530 self.collection.mode.clone(),531 &self.collection.permissions,532 permissions,533 )534 .map_err(dispatch_to_evm::<T>)?;535536 save(self)537 }538539 /// Checks that user allowed to operate with collection.540 ///541 /// @param user User address to check.542 fn allowed(&self, user: address) -> Result<bool> {543 Ok(Pallet::<T>::allowed(544 self.id,545 T::CrossAccountId::from_eth(user),546 ))547 }548549 /// Add the user to the allowed list.550 ///551 /// @param user Address of a trusted user.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: (address, uint256),568 ) -> Result<void> {569 self.consume_store_writes(1)?;570571 let caller = T::CrossAccountId::from_eth(caller);572 let user = convert_tuple_to_cross_account::<T>(user)?;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 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {581 self.consume_store_writes(1)?;582583 let caller = T::CrossAccountId::from_eth(caller);584 let user = T::CrossAccountId::from_eth(user);585 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;586 Ok(())587 }588589 /// Remove user from allowed list.590 ///591 /// @param user User cross account address.592 fn remove_from_collection_allow_list_cross(593 &mut self,594 caller: caller,595 user: (address, uint256),596 ) -> Result<void> {597 self.consume_store_writes(1)?;598599 let caller = T::CrossAccountId::from_eth(caller);600 let user = convert_tuple_to_cross_account::<T>(user)?;601 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;602 Ok(())603 }604605 /// Switch permission for minting.606 ///607 /// @param mode Enable if "true".608 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {609 self.consume_store_reads_and_writes(1, 1)?;610611 check_is_owner_or_admin(caller, self)?;612 let permissions = CollectionPermissions {613 mint_mode: Some(mode),614 ..Default::default()615 };616 self.collection.permissions = <Pallet<T>>::clamp_permissions(617 self.collection.mode.clone(),618 &self.collection.permissions,619 permissions,620 )621 .map_err(dispatch_to_evm::<T>)?;622623 save(self)624 }625626 /// Check that account is the owner or admin of the collection627 ///628 /// @param user account to verify629 /// @return "true" if account is the owner or admin630 #[solidity(rename_selector = "isOwnerOrAdmin")]631 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {632 let user = T::CrossAccountId::from_eth(user);633 Ok(self.is_owner_or_admin(&user))634 }635636 /// Check that account is the owner or admin of the collection637 ///638 /// @param user User cross account to verify639 /// @return "true" if account is the owner or admin640 fn is_owner_or_admin_cross(&self, user: (address, uint256)) -> Result<bool> {641 let user = convert_tuple_to_cross_account::<T>(user)?;642 Ok(self.is_owner_or_admin(&user))643 }644645 /// Returns collection type646 ///647 /// @return `Fungible` or `NFT` or `ReFungible`648 fn unique_collection_type(&self) -> Result<string> {649 let mode = match self.collection.mode {650 CollectionMode::Fungible(_) => "Fungible",651 CollectionMode::NFT => "NFT",652 CollectionMode::ReFungible => "ReFungible",653 };654 Ok(mode.into())655 }656657 /// Get collection owner.658 ///659 /// @return Tuple with sponsor address and his substrate mirror.660 /// If address is canonical then substrate mirror is zero and vice versa.661 fn collection_owner(&self) -> Result<(address, uint256)> {662 Ok(convert_cross_account_to_tuple::<T>(663 &T::CrossAccountId::from_sub(self.owner.clone()),664 ))665 }666667 /// Changes collection owner to another account668 ///669 /// @dev Owner can be changed only by current owner670 /// @param newOwner new owner account671 #[solidity(rename_selector = "changeCollectionOwner")]672 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {673 self.consume_store_writes(1)?;674675 let caller = T::CrossAccountId::from_eth(caller);676 let new_owner = T::CrossAccountId::from_eth(new_owner);677 self.set_owner_internal(caller, new_owner)678 .map_err(dispatch_to_evm::<T>)679 }680681 /// Get collection administrators682 ///683 /// @return Vector of tuples with admins address and his substrate mirror.684 /// If address is canonical then substrate mirror is zero and vice versa.685 fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {686 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))687 .map(|(admin, _)| crate::eth::convert_cross_account_to_tuple::<T>(&admin))688 .collect();689 Ok(result)690 }691692 /// Changes collection owner to another account693 ///694 /// @dev Owner can be changed only by current owner695 /// @param newOwner new owner cross account696 fn set_owner_cross(&mut self, caller: caller, new_owner: (address, uint256)) -> Result<void> {697 self.consume_store_writes(1)?;698699 let caller = T::CrossAccountId::from_eth(caller);700 let new_owner = convert_tuple_to_cross_account::<T>(new_owner)?;701 self.set_owner_internal(caller, new_owner)702 .map_err(dispatch_to_evm::<T>)703 }704}705706/// ### Note707/// Do not forget to add: `self.consume_store_reads(1)?;`708fn check_is_owner_or_admin<T: Config>(709 caller: caller,710 collection: &CollectionHandle<T>,711) -> Result<T::CrossAccountId> {712 let caller = T::CrossAccountId::from_eth(caller);713 collection714 .check_is_owner_or_admin(&caller)715 .map_err(dispatch_to_evm::<T>)?;716 Ok(caller)717}718719/// ### Note720/// Do not forget to add: `self.consume_store_writes(1)?;`721fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {722 collection723 .check_is_internal()724 .map_err(dispatch_to_evm::<T>)?;725 collection.save().map_err(dispatch_to_evm::<T>)?;726 Ok(())727}728729/// Contains static property keys and values.730pub mod static_property {731 use evm_coder::{732 execution::{Result, Error},733 };734 use alloc::format;735736 const EXPECT_CONVERT_ERROR: &str = "length < limit";737738 /// Keys.739 pub mod key {740 use super::*;741742 /// Key "baseURI".743 pub fn base_uri() -> up_data_structs::PropertyKey {744 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)745 }746747 /// Key "url".748 pub fn url() -> up_data_structs::PropertyKey {749 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)750 }751752 /// Key "suffix".753 pub fn suffix() -> up_data_structs::PropertyKey {754 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)755 }756757 /// Key "parentNft".758 pub fn parent_nft() -> up_data_structs::PropertyKey {759 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)760 }761 }762763 /// Convert `byte` to [`PropertyKey`].764 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {765 bytes.to_vec().try_into().map_err(|_| {766 Error::Revert(format!(767 "Property key is too long. Max length is {}.",768 up_data_structs::PropertyKey::bound()769 ))770 })771 }772773 /// Convert `bytes` to [`PropertyValue`].774 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {775 bytes.to_vec().try_into().map_err(|_| {776 Error::Revert(format!(777 "Property key is too long. Max length is {}.",778 up_data_structs::PropertyKey::bound()779 ))780 })781 }782}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_cross_account_to_tuple,38 convert_tuple_to_cross_account,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 /// The collection has been destroyed.57 CollectionDestroyed {58 /// Collection ID.59 #[indexed]60 collection_id: address,61 },62}6364/// Does not always represent a full collection, for RFT it is either65/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).66pub trait CommonEvmHandler {67 /// Raw compiled binary code of the contract stub68 const CODE: &'static [u8];6970 /// Call precompiled handle.71 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;72}7374/// @title A contract that allows you to work with collections.75#[solidity_interface(name = Collection)]76impl<T: Config> CollectionHandle<T>77where78 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,79{80 /// Set collection property.81 ///82 /// @param key Property key.83 /// @param value Propery value.84 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]85 fn set_collection_property(86 &mut self,87 caller: caller,88 key: string,89 value: bytes,90 ) -> Result<void> {91 let caller = T::CrossAccountId::from_eth(caller);92 let key = <Vec<u8>>::from(key)93 .try_into()94 .map_err(|_| "key too large")?;95 let value = value.0.try_into().map_err(|_| "value too large")?;9697 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })98 .map_err(dispatch_to_evm::<T>)99 }100101 /// Set collection properties.102 ///103 /// @param properties Vector of properties key/value pair.104 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]105 fn set_collection_properties(106 &mut self,107 caller: caller,108 properties: Vec<(string, bytes)>,109 ) -> Result<void> {110 let caller = T::CrossAccountId::from_eth(caller);111112 let properties = properties113 .into_iter()114 .map(|(key, value)| {115 let key = <Vec<u8>>::from(key)116 .try_into()117 .map_err(|_| "key too large")?;118119 let value = value.0.try_into().map_err(|_| "value too large")?;120121 Ok(Property { key, value })122 })123 .collect::<Result<Vec<_>>>()?;124125 <Pallet<T>>::set_collection_properties(self, &caller, properties)126 .map_err(dispatch_to_evm::<T>)127 }128129 /// Delete collection property.130 ///131 /// @param key Property key.132 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]133 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {134 let caller = T::CrossAccountId::from_eth(caller);135 let key = <Vec<u8>>::from(key)136 .try_into()137 .map_err(|_| "key too large")?;138139 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)140 }141142 /// Delete collection properties.143 ///144 /// @param keys Properties keys.145 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]146 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {147 let caller = T::CrossAccountId::from_eth(caller);148 let keys = keys149 .into_iter()150 .map(|key| {151 <Vec<u8>>::from(key)152 .try_into()153 .map_err(|_| Error::Revert("key too large".into()))154 })155 .collect::<Result<Vec<_>>>()?;156157 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)158 }159160 /// Get collection property.161 ///162 /// @dev Throws error if key not found.163 ///164 /// @param key Property key.165 /// @return bytes The property corresponding to the key.166 fn collection_property(&self, key: string) -> Result<bytes> {167 let key = <Vec<u8>>::from(key)168 .try_into()169 .map_err(|_| "key too large")?;170171 let props = CollectionProperties::<T>::get(self.id);172 let prop = props.get(&key).ok_or("key not found")?;173174 Ok(bytes(prop.to_vec()))175 }176177 /// Get collection properties.178 ///179 /// @param keys Properties keys. Empty keys for all propertyes.180 /// @return Vector of properties key/value pairs.181 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {182 let keys = keys183 .into_iter()184 .map(|key| {185 <Vec<u8>>::from(key)186 .try_into()187 .map_err(|_| Error::Revert("key too large".into()))188 })189 .collect::<Result<Vec<_>>>()?;190191 let properties = Pallet::<T>::filter_collection_properties(192 self.id,193 if keys.is_empty() { None } else { Some(keys) },194 )195 .map_err(dispatch_to_evm::<T>)?;196197 let properties = properties198 .into_iter()199 .map(|p| {200 let key =201 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;202 let value = bytes(p.value.to_vec());203 Ok((key, value))204 })205 .collect::<Result<Vec<_>>>()?;206 Ok(properties)207 }208209 /// Set the sponsor of the collection.210 ///211 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.212 ///213 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.214 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {215 self.consume_store_reads_and_writes(1, 1)?;216217 check_is_owner_or_admin(caller, self)?;218219 let sponsor = T::CrossAccountId::from_eth(sponsor);220 self.set_sponsor(sponsor.as_sub().clone())221 .map_err(dispatch_to_evm::<T>)?;222 save(self)223 }224225 /// Set the sponsor of the collection.226 ///227 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.228 ///229 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.230 fn set_collection_sponsor_cross(231 &mut self,232 caller: caller,233 sponsor: (address, uint256),234 ) -> Result<void> {235 self.consume_store_reads_and_writes(1, 1)?;236237 check_is_owner_or_admin(caller, self)?;238239 let sponsor = convert_tuple_to_cross_account::<T>(sponsor)?;240 self.set_sponsor(sponsor.as_sub().clone())241 .map_err(dispatch_to_evm::<T>)?;242 save(self)243 }244245 /// Whether there is a pending sponsor.246 fn has_collection_pending_sponsor(&self) -> Result<bool> {247 Ok(matches!(248 self.collection.sponsorship,249 SponsorshipState::Unconfirmed(_)250 ))251 }252253 /// Collection sponsorship confirmation.254 ///255 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.256 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {257 self.consume_store_writes(1)?;258259 let caller = T::CrossAccountId::from_eth(caller);260 if !self261 .confirm_sponsorship(caller.as_sub())262 .map_err(dispatch_to_evm::<T>)?263 {264 return Err("caller is not set as sponsor".into());265 }266 save(self)267 }268269 /// Remove collection sponsor.270 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {271 self.consume_store_reads_and_writes(1, 1)?;272 check_is_owner_or_admin(caller, self)?;273 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;274 save(self)275 }276277 /// Get current sponsor.278 ///279 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.280 fn collection_sponsor(&self) -> Result<(address, uint256)> {281 let sponsor = match self.collection.sponsorship.sponsor() {282 Some(sponsor) => sponsor,283 None => return Ok(Default::default()),284 };285 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());286 let result: (address, uint256) = if sponsor.is_canonical_substrate() {287 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);288 (Default::default(), sponsor)289 } else {290 let sponsor = *sponsor.as_eth();291 (sponsor, Default::default())292 };293 Ok(result)294 }295296 /// Set limits for the collection.297 /// @dev Throws error if limit not found.298 /// @param limit Name of the limit. Valid names:299 /// "accountTokenOwnershipLimit",300 /// "sponsoredDataSize",301 /// "sponsoredDataRateLimit",302 /// "tokenLimit",303 /// "sponsorTransferTimeout",304 /// "sponsorApproveTimeout"305 /// @param value Value of the limit.306 #[solidity(rename_selector = "setCollectionLimit")]307 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {308 self.consume_store_reads_and_writes(1, 1)?;309310 check_is_owner_or_admin(caller, self)?;311 let mut limits = self.limits.clone();312313 match limit.as_str() {314 "accountTokenOwnershipLimit" => {315 limits.account_token_ownership_limit = Some(value);316 }317 "sponsoredDataSize" => {318 limits.sponsored_data_size = Some(value);319 }320 "sponsoredDataRateLimit" => {321 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));322 }323 "tokenLimit" => {324 limits.token_limit = Some(value);325 }326 "sponsorTransferTimeout" => {327 limits.sponsor_transfer_timeout = Some(value);328 }329 "sponsorApproveTimeout" => {330 limits.sponsor_approve_timeout = Some(value);331 }332 _ => {333 return Err(Error::Revert(format!(334 "unknown integer limit \"{}\"",335 limit336 )))337 }338 }339 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)340 .map_err(dispatch_to_evm::<T>)?;341 save(self)342 }343344 /// Set limits for the collection.345 /// @dev Throws error if limit not found.346 /// @param limit Name of the limit. Valid names:347 /// "ownerCanTransfer",348 /// "ownerCanDestroy",349 /// "transfersEnabled"350 /// @param value Value of the limit.351 #[solidity(rename_selector = "setCollectionLimit")]352 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {353 self.consume_store_reads_and_writes(1, 1)?;354355 check_is_owner_or_admin(caller, self)?;356 let mut limits = self.limits.clone();357358 match limit.as_str() {359 "ownerCanTransfer" => {360 limits.owner_can_transfer = Some(value);361 }362 "ownerCanDestroy" => {363 limits.owner_can_destroy = Some(value);364 }365 "transfersEnabled" => {366 limits.transfers_enabled = Some(value);367 }368 _ => {369 return Err(Error::Revert(format!(370 "unknown boolean limit \"{}\"",371 limit372 )))373 }374 }375 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)376 .map_err(dispatch_to_evm::<T>)?;377 save(self)378 }379380 /// Get contract address.381 fn contract_address(&self) -> Result<address> {382 Ok(crate::eth::collection_id_to_address(self.id))383 }384385 /// Add collection admin.386 /// @param newAdmin Cross account administrator address.387 fn add_collection_admin_cross(388 &mut self,389 caller: caller,390 new_admin: (address, uint256),391 ) -> Result<void> {392 self.consume_store_writes(2)?;393394 let caller = T::CrossAccountId::from_eth(caller);395 let new_admin = convert_tuple_to_cross_account::<T>(new_admin)?;396 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;397 Ok(())398 }399400 /// Remove collection admin.401 /// @param admin Cross account administrator address.402 fn remove_collection_admin_cross(403 &mut self,404 caller: caller,405 admin: (address, uint256),406 ) -> Result<void> {407 self.consume_store_writes(2)?;408409 let caller = T::CrossAccountId::from_eth(caller);410 let admin = convert_tuple_to_cross_account::<T>(admin)?;411 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;412 Ok(())413 }414415 /// Add collection admin.416 /// @param newAdmin Address of the added administrator.417 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {418 self.consume_store_writes(2)?;419420 let caller = T::CrossAccountId::from_eth(caller);421 let new_admin = T::CrossAccountId::from_eth(new_admin);422 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;423 Ok(())424 }425426 /// Remove collection admin.427 ///428 /// @param admin Address of the removed administrator.429 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {430 self.consume_store_writes(2)?;431432 let caller = T::CrossAccountId::from_eth(caller);433 let admin = T::CrossAccountId::from_eth(admin);434 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;435 Ok(())436 }437438 /// Toggle accessibility of collection nesting.439 ///440 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'441 #[solidity(rename_selector = "setCollectionNesting")]442 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {443 self.consume_store_reads_and_writes(1, 1)?;444445 check_is_owner_or_admin(caller, self)?;446447 let mut permissions = self.collection.permissions.clone();448 let mut nesting = permissions.nesting().clone();449 nesting.token_owner = enable;450 nesting.restricted = None;451 permissions.nesting = Some(nesting);452453 self.collection.permissions = <Pallet<T>>::clamp_permissions(454 self.collection.mode.clone(),455 &self.collection.permissions,456 permissions,457 )458 .map_err(dispatch_to_evm::<T>)?;459460 save(self)461 }462463 /// Toggle accessibility of collection nesting.464 ///465 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'466 /// @param collections Addresses of collections that will be available for nesting.467 #[solidity(rename_selector = "setCollectionNesting")]468 fn set_nesting(469 &mut self,470 caller: caller,471 enable: bool,472 collections: Vec<address>,473 ) -> Result<void> {474 self.consume_store_reads_and_writes(1, 1)?;475476 if collections.is_empty() {477 return Err("no addresses provided".into());478 }479 check_is_owner_or_admin(caller, self)?;480481 let mut permissions = self.collection.permissions.clone();482 match enable {483 false => {484 let mut nesting = permissions.nesting().clone();485 nesting.token_owner = false;486 nesting.restricted = None;487 permissions.nesting = Some(nesting);488 }489 true => {490 let mut bv = OwnerRestrictedSet::new();491 for i in collections {492 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {493 Error::Revert("Can't convert address into collection id".into())494 })?)495 .map_err(|_| "too many collections")?;496 }497 let mut nesting = permissions.nesting().clone();498 nesting.token_owner = true;499 nesting.restricted = Some(bv);500 permissions.nesting = Some(nesting);501 }502 };503504 self.collection.permissions = <Pallet<T>>::clamp_permissions(505 self.collection.mode.clone(),506 &self.collection.permissions,507 permissions,508 )509 .map_err(dispatch_to_evm::<T>)?;510511 save(self)512 }513514 /// Set the collection access method.515 /// @param mode Access mode516 /// 0 for Normal517 /// 1 for AllowList518 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {519 self.consume_store_reads_and_writes(1, 1)?;520521 check_is_owner_or_admin(caller, self)?;522 let permissions = CollectionPermissions {523 access: Some(match mode {524 0 => AccessMode::Normal,525 1 => AccessMode::AllowList,526 _ => return Err("not supported access mode".into()),527 }),528 ..Default::default()529 };530 self.collection.permissions = <Pallet<T>>::clamp_permissions(531 self.collection.mode.clone(),532 &self.collection.permissions,533 permissions,534 )535 .map_err(dispatch_to_evm::<T>)?;536537 save(self)538 }539540 /// Checks that user allowed to operate with collection.541 ///542 /// @param user User address to check.543 fn allowed(&self, user: address) -> Result<bool> {544 Ok(Pallet::<T>::allowed(545 self.id,546 T::CrossAccountId::from_eth(user),547 ))548 }549550 /// Add the user to the allowed list.551 ///552 /// @param user Address of a trusted user.553 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {554 self.consume_store_writes(1)?;555556 let caller = T::CrossAccountId::from_eth(caller);557 let user = T::CrossAccountId::from_eth(user);558 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;559 Ok(())560 }561562 /// Add user to allowed list.563 ///564 /// @param user User cross account address.565 fn add_to_collection_allow_list_cross(566 &mut self,567 caller: caller,568 user: (address, uint256),569 ) -> Result<void> {570 self.consume_store_writes(1)?;571572 let caller = T::CrossAccountId::from_eth(caller);573 let user = convert_tuple_to_cross_account::<T>(user)?;574 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;575 Ok(())576 }577578 /// Remove the user from the allowed list.579 ///580 /// @param user Address of a removed user.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: (address, uint256),597 ) -> Result<void> {598 self.consume_store_writes(1)?;599600 let caller = T::CrossAccountId::from_eth(caller);601 let user = convert_tuple_to_cross_account::<T>(user)?;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 check_is_owner_or_admin(caller, self)?;613 let permissions = CollectionPermissions {614 mint_mode: Some(mode),615 ..Default::default()616 };617 self.collection.permissions = <Pallet<T>>::clamp_permissions(618 self.collection.mode.clone(),619 &self.collection.permissions,620 permissions,621 )622 .map_err(dispatch_to_evm::<T>)?;623624 save(self)625 }626627 /// Check that account is the owner or admin of the collection628 ///629 /// @param user account to verify630 /// @return "true" if account is the owner or admin631 #[solidity(rename_selector = "isOwnerOrAdmin")]632 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {633 let user = T::CrossAccountId::from_eth(user);634 Ok(self.is_owner_or_admin(&user))635 }636637 /// Check that account is the owner or admin of the collection638 ///639 /// @param user User cross account to verify640 /// @return "true" if account is the owner or admin641 fn is_owner_or_admin_cross(&self, user: (address, uint256)) -> Result<bool> {642 let user = convert_tuple_to_cross_account::<T>(user)?;643 Ok(self.is_owner_or_admin(&user))644 }645646 /// Returns collection type647 ///648 /// @return `Fungible` or `NFT` or `ReFungible`649 fn unique_collection_type(&self) -> Result<string> {650 let mode = match self.collection.mode {651 CollectionMode::Fungible(_) => "Fungible",652 CollectionMode::NFT => "NFT",653 CollectionMode::ReFungible => "ReFungible",654 };655 Ok(mode.into())656 }657658 /// Get collection owner.659 ///660 /// @return Tuple with sponsor address and his substrate mirror.661 /// If address is canonical then substrate mirror is zero and vice versa.662 fn collection_owner(&self) -> Result<(address, uint256)> {663 Ok(convert_cross_account_to_tuple::<T>(664 &T::CrossAccountId::from_sub(self.owner.clone()),665 ))666 }667668 /// Changes collection owner to another account669 ///670 /// @dev Owner can be changed only by current owner671 /// @param newOwner new owner account672 #[solidity(rename_selector = "changeCollectionOwner")]673 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {674 self.consume_store_writes(1)?;675676 let caller = T::CrossAccountId::from_eth(caller);677 let new_owner = T::CrossAccountId::from_eth(new_owner);678 self.set_owner_internal(caller, new_owner)679 .map_err(dispatch_to_evm::<T>)680 }681682 /// Get collection administrators683 ///684 /// @return Vector of tuples with admins address and his substrate mirror.685 /// If address is canonical then substrate mirror is zero and vice versa.686 fn collection_admins(&self) -> Result<Vec<(address, uint256)>> {687 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))688 .map(|(admin, _)| crate::eth::convert_cross_account_to_tuple::<T>(&admin))689 .collect();690 Ok(result)691 }692693 /// Changes collection owner to another account694 ///695 /// @dev Owner can be changed only by current owner696 /// @param newOwner new owner cross account697 fn set_owner_cross(&mut self, caller: caller, new_owner: (address, uint256)) -> Result<void> {698 self.consume_store_writes(1)?;699700 let caller = T::CrossAccountId::from_eth(caller);701 let new_owner = convert_tuple_to_cross_account::<T>(new_owner)?;702 self.set_owner_internal(caller, new_owner)703 .map_err(dispatch_to_evm::<T>)704 }705}706707/// ### Note708/// Do not forget to add: `self.consume_store_reads(1)?;`709fn check_is_owner_or_admin<T: Config>(710 caller: caller,711 collection: &CollectionHandle<T>,712) -> Result<T::CrossAccountId> {713 let caller = T::CrossAccountId::from_eth(caller);714 collection715 .check_is_owner_or_admin(&caller)716 .map_err(dispatch_to_evm::<T>)?;717 Ok(caller)718}719720/// ### Note721/// Do not forget to add: `self.consume_store_writes(1)?;`722fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {723 collection724 .check_is_internal()725 .map_err(dispatch_to_evm::<T>)?;726 collection.save().map_err(dispatch_to_evm::<T>)?;727 Ok(())728}729730/// Contains static property keys and values.731pub mod static_property {732 use evm_coder::{733 execution::{Result, Error},734 };735 use alloc::format;736737 const EXPECT_CONVERT_ERROR: &str = "length < limit";738739 /// Keys.740 pub mod key {741 use super::*;742743 /// Key "baseURI".744 pub fn base_uri() -> up_data_structs::PropertyKey {745 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)746 }747748 /// Key "url".749 pub fn url() -> up_data_structs::PropertyKey {750 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)751 }752753 /// Key "suffix".754 pub fn suffix() -> up_data_structs::PropertyKey {755 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)756 }757758 /// Key "parentNft".759 pub fn parent_nft() -> up_data_structs::PropertyKey {760 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)761 }762 }763764 /// Convert `byte` to [`PropertyKey`].765 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {766 bytes.to_vec().try_into().map_err(|_| {767 Error::Revert(format!(768 "Property key is too long. Max length is {}.",769 up_data_structs::PropertyKey::bound()770 ))771 })772 }773774 /// Convert `bytes` to [`PropertyValue`].775 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {776 bytes.to_vec().try_into().map_err(|_| {777 Error::Revert(format!(778 "Property key is too long. Max length is {}.",779 up_data_structs::PropertyKey::bound()780 ))781 })782 }783}pallets/common/src/weights.rsdiffbeforeafterboth--- a/pallets/common/src/weights.rs
+++ b/pallets/common/src/weights.rs
@@ -26,6 +26,7 @@
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
+#![allow(missing_docs)]
#![allow(clippy::unnecessary_cast)]
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -172,7 +172,7 @@
>;
#[pallet::event]
- #[pallet::generate_deposit(pub fn deposit_event)]
+ #[pallet::generate_deposit(fn deposit_event)]
pub enum Event<T: Config> {
/// Contract sponsor was set.
ContractSponsorSet(
@@ -350,6 +350,7 @@
pub fn sponsoring_mode(contract: H160) -> SponsoringModeT {
<SponsoringMode<T>>::get(contract)
.or_else(|| {
+ #[allow(deprecated)]
<SelfSponsoring<T>>::get(contract).then(|| SponsoringModeT::Allowlisted)
})
.unwrap_or_default()
@@ -362,6 +363,7 @@
} else {
<SponsoringMode<T>>::insert(contract, mode);
}
+ #[allow(deprecated)]
<SelfSponsoring<T>>::remove(contract)
}
@@ -424,6 +426,7 @@
_ => return None,
})
}
+ #[allow(dead_code)]
fn to_eth(self) -> u8 {
match self {
SponsoringModeT::Disabled => 0,
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -137,7 +137,6 @@
/// for the convenience of database access. Notably contains the token metadata.
#[struct_versioning::versioned(version = 2, upper)]
#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
-#[deprecated(since = "0.2.0", note = "ItemData is no more contains usefull data")]
pub struct ItemData {
pub const_data: BoundedVec<u8, CustomDataLimit>,
@@ -279,7 +278,8 @@
fn on_runtime_upgrade() -> Weight {
let storage_version = StorageVersion::get::<Pallet<T>>();
if storage_version < StorageVersion::new(2) {
- <TokenData<T>>::remove_all(None);
+ #[allow(deprecated)]
+ let _ = <TokenData<T>>::clear(u32::MAX, None);
}
StorageVersion::new(2).put::<Pallet<T>>();