difftreelog
misk: Remove some warnings. Add over_max_size test
in: master
12 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2347,6 +2347,7 @@
"sha3-const",
"similar-asserts",
"sp-std",
+ "trybuild",
]
[[package]]
@@ -12671,6 +12672,21 @@
]
[[package]]
+name = "trybuild"
+version = "1.0.71"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea496675d71016e9bc76aa42d87f16aefd95447cc5818e671e12b2d7e269075d"
+dependencies = [
+ "glob",
+ "once_cell",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "termcolor",
+ "toml",
+]
+
+[[package]]
name = "tt-call"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
crates/evm-coder/Cargo.tomldiffbeforeafterboth--- a/crates/evm-coder/Cargo.toml
+++ b/crates/evm-coder/Cargo.toml
@@ -29,6 +29,7 @@
hex-literal = "0.3.4"
similar-asserts = "1.4.2"
concat-idents = "1.1.3"
+trybuild = "1.0"
[features]
default = ["std"]
crates/evm-coder/src/custom_signature.rsdiffbeforeafterboth--- a/crates/evm-coder/src/custom_signature.rs
+++ b/crates/evm-coder/src/custom_signature.rs
@@ -423,15 +423,6 @@
assert_eq!(<MaxSize>::name(), "!".repeat(SIGNATURE_SIZE_LIMIT));
}
- // This test must NOT compile with "index out of bounds"!
- // #[test]
- // fn over_max_size() {
- // assert_eq!(
- // <Vec<MaxSize>>::name(),
- // "!".repeat(SIGNATURE_SIZE_LIMIT) + "[]"
- // );
- // }
-
#[test]
fn make_func_without_args() {
const SIG: FunctionSignature = make_signature!(
@@ -498,4 +489,10 @@
fn shift() {
assert_eq!(<(u32,)>::name(), "(uint32)");
}
+
+ #[test]
+ fn over_max_size() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/build_failed/custom_signature_over_max_size.rs");
+ }
}
crates/evm-coder/tests/build_failed/custom_signature_over_max_size.rsdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/custom_signature_over_max_size.rs
@@ -0,0 +1,33 @@
+#![allow(dead_code)]
+use std::str::from_utf8;
+
+use evm_coder::{
+ make_signature,
+ custom_signature::{SignatureUnit, SIGNATURE_SIZE_LIMIT},
+};
+
+trait Name {
+ const SIGNATURE: SignatureUnit;
+
+ fn name() -> &'static str {
+ from_utf8(&Self::SIGNATURE.data[..Self::SIGNATURE.len]).expect("bad utf-8")
+ }
+}
+
+impl<T: Name> Name for Vec<T> {
+ evm_coder::make_signature!(new nameof(T) fixed("[]"));
+}
+
+struct MaxSize();
+impl Name for MaxSize {
+ const SIGNATURE: SignatureUnit = SignatureUnit {
+ data: [b'!'; SIGNATURE_SIZE_LIMIT],
+ len: SIGNATURE_SIZE_LIMIT,
+ };
+}
+
+const NAME: SignatureUnit = <Vec<MaxSize>>::SIGNATURE;
+
+fn main() {
+ assert!(false);
+}
crates/evm-coder/tests/build_failed/custom_signature_over_max_size.stderrdiffbeforeafterboth--- /dev/null
+++ b/crates/evm-coder/tests/build_failed/custom_signature_over_max_size.stderr
@@ -0,0 +1,19 @@
+error: any use of this value will cause an error
+ --> tests/build_failed/custom_signature_over_max_size.rs:18:2
+ |
+18 | evm_coder::make_signature!(new nameof(T) fixed("[]"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ index out of bounds: the length is 256 but the index is 256
+ |
+ = note: `#[deny(const_err)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
+ = note: this error originates in the macro `make_signature` which comes from the expansion of the macro `evm_coder::make_signature` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: any use of this value will cause an error
+ --> tests/build_failed/custom_signature_over_max_size.rs:29:29
+ |
+29 | const NAME: SignatureUnit = <Vec<MaxSize>>::SIGNATURE;
+ | ------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
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 custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},25 make_signature,26};27pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::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 convert_cross_account_to_uint256, convert_cross_account_to_tuple,40 convert_tuple_to_cross_account,41 },42 weights::WeightInfo,43};4445/// Events for ethereum collection helper.46#[derive(ToLog)]47pub enum CollectionHelpersEvents {48 /// The collection has been created.49 CollectionCreated {50 /// Collection owner.51 #[indexed]52 owner: address,5354 /// Collection ID.55 #[indexed]56 collection_id: address,57 },58 /// The collection has been destroyed.59 CollectionDestroyed {60 /// Collection ID.61 #[indexed]62 collection_id: address,63 },64}6566/// Does not always represent a full collection, for RFT it is either67/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).68pub trait CommonEvmHandler {69 const CODE: &'static [u8];7071 /// Call precompiled handle.72 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;73}7475/// @title A contract that allows you to work with collections.76#[solidity_interface(name = Collection)]77impl<T: Config> CollectionHandle<T>78where79 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,80{81 /// Set collection property.82 ///83 /// @param key Property key.84 /// @param value Propery value.85 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]86 fn set_collection_property(87 &mut self,88 caller: caller,89 key: string,90 value: bytes,91 ) -> Result<void> {92 let caller = T::CrossAccountId::from_eth(caller);93 let key = <Vec<u8>>::from(key)94 .try_into()95 .map_err(|_| "key too large")?;96 let value = value.0.try_into().map_err(|_| "value too large")?;9798 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })99 .map_err(dispatch_to_evm::<T>)100 }101102 /// Set collection properties.103 ///104 /// @param properties Vector of properties key/value pair.105 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]106 fn set_collection_properties(107 &mut self,108 caller: caller,109 properties: Vec<(string, bytes)>,110 ) -> Result<void> {111 let caller = T::CrossAccountId::from_eth(caller);112113 let properties = properties114 .into_iter()115 .map(|(key, value)| {116 let key = <Vec<u8>>::from(key)117 .try_into()118 .map_err(|_| "key too large")?;119120 let value = value.0.try_into().map_err(|_| "value too large")?;121122 Ok(Property { key, value })123 })124 .collect::<Result<Vec<_>>>()?;125126 <Pallet<T>>::set_collection_properties(self, &caller, properties)127 .map_err(dispatch_to_evm::<T>)128 }129130 /// Delete collection property.131 ///132 /// @param key Property key.133 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]134 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {135 let caller = T::CrossAccountId::from_eth(caller);136 let key = <Vec<u8>>::from(key)137 .try_into()138 .map_err(|_| "key too large")?;139140 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)141 }142143 /// Delete collection properties.144 ///145 /// @param keys Properties keys.146 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]147 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {148 let caller = T::CrossAccountId::from_eth(caller);149 let keys = keys150 .into_iter()151 .map(|key| {152 <Vec<u8>>::from(key)153 .try_into()154 .map_err(|_| Error::Revert("key too large".into()))155 })156 .collect::<Result<Vec<_>>>()?;157158 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)159 }160161 /// Get collection property.162 ///163 /// @dev Throws error if key not found.164 ///165 /// @param key Property key.166 /// @return bytes The property corresponding to the key.167 fn collection_property(&self, key: string) -> Result<bytes> {168 let key = <Vec<u8>>::from(key)169 .try_into()170 .map_err(|_| "key too large")?;171172 let props = CollectionProperties::<T>::get(self.id);173 let prop = props.get(&key).ok_or("key not found")?;174175 Ok(bytes(prop.to_vec()))176 }177178 /// Get collection properties.179 ///180 /// @param keys Properties keys. Empty keys for all propertyes.181 /// @return Vector of properties key/value pairs.182 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {183 let keys = keys184 .into_iter()185 .map(|key| {186 <Vec<u8>>::from(key)187 .try_into()188 .map_err(|_| Error::Revert("key too large".into()))189 })190 .collect::<Result<Vec<_>>>()?;191192 let properties = Pallet::<T>::filter_collection_properties(193 self.id,194 if keys.is_empty() { None } else { Some(keys) },195 )196 .map_err(dispatch_to_evm::<T>)?;197198 let properties = properties199 .into_iter()200 .map(|p| {201 let key =202 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;203 let value = bytes(p.value.to_vec());204 Ok((key, value))205 })206 .collect::<Result<Vec<_>>>()?;207 Ok(properties)208 }209210 /// Set the sponsor of the collection.211 ///212 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.213 ///214 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.215 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {216 self.consume_store_reads_and_writes(1, 1)?;217218 check_is_owner_or_admin(caller, self)?;219220 let sponsor = T::CrossAccountId::from_eth(sponsor);221 self.set_sponsor(sponsor.as_sub().clone())222 .map_err(dispatch_to_evm::<T>)?;223 save(self)224 }225226 /// Set the sponsor of the collection.227 ///228 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.229 ///230 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.231 fn set_collection_sponsor_cross(232 &mut self,233 caller: caller,234 sponsor: EthCrossAccount,235 ) -> Result<void> {236 self.consume_store_reads_and_writes(1, 1)?;237238 check_is_owner_or_admin(caller, self)?;239240 let sponsor = sponsor.into_sub_cross_account::<T>()?;241 self.set_sponsor(sponsor.as_sub().clone())242 .map_err(dispatch_to_evm::<T>)?;243 save(self)244 }245246 /// Whether there is a pending sponsor.247 fn has_collection_pending_sponsor(&self) -> Result<bool> {248 Ok(matches!(249 self.collection.sponsorship,250 SponsorshipState::Unconfirmed(_)251 ))252 }253254 /// Collection sponsorship confirmation.255 ///256 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.257 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {258 self.consume_store_writes(1)?;259260 let caller = T::CrossAccountId::from_eth(caller);261 if !self262 .confirm_sponsorship(caller.as_sub())263 .map_err(dispatch_to_evm::<T>)?264 {265 return Err("caller is not set as sponsor".into());266 }267 save(self)268 }269270 /// Remove collection sponsor.271 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {272 self.consume_store_reads_and_writes(1, 1)?;273 check_is_owner_or_admin(caller, self)?;274 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;275 save(self)276 }277278 /// Get current sponsor.279 ///280 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.281 fn collection_sponsor(&self) -> Result<(address, uint256)> {282 let sponsor = match self.collection.sponsorship.sponsor() {283 Some(sponsor) => sponsor,284 None => return Ok(Default::default()),285 };286 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());287 let result: (address, uint256) = if sponsor.is_canonical_substrate() {288 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);289 (Default::default(), sponsor)290 } else {291 let sponsor = *sponsor.as_eth();292 (sponsor, Default::default())293 };294 Ok(result)295 }296297 /// Set limits for the collection.298 /// @dev Throws error if limit not found.299 /// @param limit Name of the limit. Valid names:300 /// "accountTokenOwnershipLimit",301 /// "sponsoredDataSize",302 /// "sponsoredDataRateLimit",303 /// "tokenLimit",304 /// "sponsorTransferTimeout",305 /// "sponsorApproveTimeout"306 /// @param value Value of the limit.307 #[solidity(rename_selector = "setCollectionLimit")]308 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {309 self.consume_store_reads_and_writes(1, 1)?;310311 check_is_owner_or_admin(caller, self)?;312 let mut limits = self.limits.clone();313314 match limit.as_str() {315 "accountTokenOwnershipLimit" => {316 limits.account_token_ownership_limit = Some(value);317 }318 "sponsoredDataSize" => {319 limits.sponsored_data_size = Some(value);320 }321 "sponsoredDataRateLimit" => {322 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));323 }324 "tokenLimit" => {325 limits.token_limit = Some(value);326 }327 "sponsorTransferTimeout" => {328 limits.sponsor_transfer_timeout = Some(value);329 }330 "sponsorApproveTimeout" => {331 limits.sponsor_approve_timeout = Some(value);332 }333 _ => {334 return Err(Error::Revert(format!(335 "unknown integer limit \"{}\"",336 limit337 )))338 }339 }340 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)341 .map_err(dispatch_to_evm::<T>)?;342 save(self)343 }344345 /// Set limits for the collection.346 /// @dev Throws error if limit not found.347 /// @param limit Name of the limit. Valid names:348 /// "ownerCanTransfer",349 /// "ownerCanDestroy",350 /// "transfersEnabled"351 /// @param value Value of the limit.352 #[solidity(rename_selector = "setCollectionLimit")]353 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {354 self.consume_store_reads_and_writes(1, 1)?;355356 check_is_owner_or_admin(caller, self)?;357 let mut limits = self.limits.clone();358359 match limit.as_str() {360 "ownerCanTransfer" => {361 limits.owner_can_transfer = Some(value);362 }363 "ownerCanDestroy" => {364 limits.owner_can_destroy = Some(value);365 }366 "transfersEnabled" => {367 limits.transfers_enabled = Some(value);368 }369 _ => {370 return Err(Error::Revert(format!(371 "unknown boolean limit \"{}\"",372 limit373 )))374 }375 }376 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)377 .map_err(dispatch_to_evm::<T>)?;378 save(self)379 }380381 /// Get contract address.382 fn contract_address(&self) -> Result<address> {383 Ok(crate::eth::collection_id_to_address(self.id))384 }385386 /// Add collection admin.387 /// @param newAdmin Cross account administrator address.388 fn add_collection_admin_cross(389 &mut self,390 caller: caller,391 new_admin: EthCrossAccount,392 ) -> Result<void> {393 self.consume_store_writes(2)?;394395 let caller = T::CrossAccountId::from_eth(caller);396 let new_admin = new_admin.into_sub_cross_account::<T>()?;397 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;398 Ok(())399 }400401 /// Remove collection admin.402 /// @param admin Cross account administrator address.403 fn remove_collection_admin_cross(404 &mut self,405 caller: caller,406 admin: EthCrossAccount,407 ) -> Result<void> {408 self.consume_store_writes(2)?;409410 let caller = T::CrossAccountId::from_eth(caller);411 let admin = admin.into_sub_cross_account::<T>()?;412 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;413 Ok(())414 }415416 /// Add collection admin.417 /// @param newAdmin Address of the added administrator.418 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {419 self.consume_store_writes(2)?;420421 let caller = T::CrossAccountId::from_eth(caller);422 let new_admin = T::CrossAccountId::from_eth(new_admin);423 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;424 Ok(())425 }426427 /// Remove collection admin.428 ///429 /// @param admin Address of the removed administrator.430 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {431 self.consume_store_writes(2)?;432433 let caller = T::CrossAccountId::from_eth(caller);434 let admin = T::CrossAccountId::from_eth(admin);435 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;436 Ok(())437 }438439 /// Toggle accessibility of collection nesting.440 ///441 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'442 #[solidity(rename_selector = "setCollectionNesting")]443 fn set_nesting_bool(&mut self, caller: caller, enable: bool) -> Result<void> {444 self.consume_store_reads_and_writes(1, 1)?;445446 check_is_owner_or_admin(caller, self)?;447448 let mut permissions = self.collection.permissions.clone();449 let mut nesting = permissions.nesting().clone();450 nesting.token_owner = enable;451 nesting.restricted = None;452 permissions.nesting = Some(nesting);453454 self.collection.permissions = <Pallet<T>>::clamp_permissions(455 self.collection.mode.clone(),456 &self.collection.permissions,457 permissions,458 )459 .map_err(dispatch_to_evm::<T>)?;460461 save(self)462 }463464 /// Toggle accessibility of collection nesting.465 ///466 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'467 /// @param collections Addresses of collections that will be available for nesting.468 #[solidity(rename_selector = "setCollectionNesting")]469 fn set_nesting(470 &mut self,471 caller: caller,472 enable: bool,473 collections: Vec<address>,474 ) -> Result<void> {475 self.consume_store_reads_and_writes(1, 1)?;476477 if collections.is_empty() {478 return Err("no addresses provided".into());479 }480 check_is_owner_or_admin(caller, self)?;481482 let mut permissions = self.collection.permissions.clone();483 match enable {484 false => {485 let mut nesting = permissions.nesting().clone();486 nesting.token_owner = false;487 nesting.restricted = None;488 permissions.nesting = Some(nesting);489 }490 true => {491 let mut bv = OwnerRestrictedSet::new();492 for i in collections {493 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {494 Error::Revert("Can't convert address into collection id".into())495 })?)496 .map_err(|_| "too many collections")?;497 }498 let mut nesting = permissions.nesting().clone();499 nesting.token_owner = true;500 nesting.restricted = Some(bv);501 permissions.nesting = Some(nesting);502 }503 };504505 self.collection.permissions = <Pallet<T>>::clamp_permissions(506 self.collection.mode.clone(),507 &self.collection.permissions,508 permissions,509 )510 .map_err(dispatch_to_evm::<T>)?;511512 save(self)513 }514515 /// Set the collection access method.516 /// @param mode Access mode517 /// 0 for Normal518 /// 1 for AllowList519 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {520 self.consume_store_reads_and_writes(1, 1)?;521522 check_is_owner_or_admin(caller, self)?;523 let permissions = CollectionPermissions {524 access: Some(match mode {525 0 => AccessMode::Normal,526 1 => AccessMode::AllowList,527 _ => return Err("not supported access mode".into()),528 }),529 ..Default::default()530 };531 self.collection.permissions = <Pallet<T>>::clamp_permissions(532 self.collection.mode.clone(),533 &self.collection.permissions,534 permissions,535 )536 .map_err(dispatch_to_evm::<T>)?;537538 save(self)539 }540541 /// Checks that user allowed to operate with collection.542 ///543 /// @param user User address to check.544 fn allowed(&self, user: address) -> Result<bool> {545 Ok(Pallet::<T>::allowed(546 self.id,547 T::CrossAccountId::from_eth(user),548 ))549 }550551 /// Add the user to the allowed list.552 ///553 /// @param user Address of a trusted user.554 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {555 self.consume_store_writes(1)?;556557 let caller = T::CrossAccountId::from_eth(caller);558 let user = T::CrossAccountId::from_eth(user);559 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;560 Ok(())561 }562563 /// Add user to allowed list.564 ///565 /// @param user User cross account address.566 fn add_to_collection_allow_list_cross(567 &mut self,568 caller: caller,569 user: EthCrossAccount,570 ) -> Result<void> {571 self.consume_store_writes(1)?;572573 let caller = T::CrossAccountId::from_eth(caller);574 let user = user.into_sub_cross_account::<T>()?;575 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;576 Ok(())577 }578579 /// Remove the user from the allowed list.580 ///581 /// @param user Address of a removed user.582 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {583 self.consume_store_writes(1)?;584585 let caller = T::CrossAccountId::from_eth(caller);586 let user = T::CrossAccountId::from_eth(user);587 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;588 Ok(())589 }590591 /// Remove user from allowed list.592 ///593 /// @param user User cross account address.594 fn remove_from_collection_allow_list_cross(595 &mut self,596 caller: caller,597 user: EthCrossAccount,598 ) -> Result<void> {599 self.consume_store_writes(1)?;600601 let caller = T::CrossAccountId::from_eth(caller);602 let user = user.into_sub_cross_account::<T>()?;603 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;604 Ok(())605 }606607 /// Switch permission for minting.608 ///609 /// @param mode Enable if "true".610 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {611 self.consume_store_reads_and_writes(1, 1)?;612613 check_is_owner_or_admin(caller, self)?;614 let permissions = CollectionPermissions {615 mint_mode: Some(mode),616 ..Default::default()617 };618 self.collection.permissions = <Pallet<T>>::clamp_permissions(619 self.collection.mode.clone(),620 &self.collection.permissions,621 permissions,622 )623 .map_err(dispatch_to_evm::<T>)?;624625 save(self)626 }627628 /// Check that account is the owner or admin of the collection629 ///630 /// @param user account to verify631 /// @return "true" if account is the owner or admin632 #[solidity(rename_selector = "isOwnerOrAdmin")]633 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {634 let user = T::CrossAccountId::from_eth(user);635 Ok(self.is_owner_or_admin(&user))636 }637638 /// Check that account is the owner or admin of the collection639 ///640 /// @param user User cross account to verify641 /// @return "true" if account is the owner or admin642 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {643 let user = user.into_sub_cross_account::<T>()?;644 Ok(self.is_owner_or_admin(&user))645 }646647 /// Returns collection type648 ///649 /// @return `Fungible` or `NFT` or `ReFungible`650 fn unique_collection_type(&self) -> Result<string> {651 let mode = match self.collection.mode {652 CollectionMode::Fungible(_) => "Fungible",653 CollectionMode::NFT => "NFT",654 CollectionMode::ReFungible => "ReFungible",655 };656 Ok(mode.into())657 }658659 /// Get collection owner.660 ///661 /// @return Tuble with sponsor address and his substrate mirror.662 /// If address is canonical then substrate mirror is zero and vice versa.663 fn collection_owner(&self) -> Result<EthCrossAccount> {664 Ok(EthCrossAccount::from_sub_cross_account::<T>(665 &T::CrossAccountId::from_sub(self.owner.clone()),666 ))667 }668669 /// Changes collection owner to another account670 ///671 /// @dev Owner can be changed only by current owner672 /// @param newOwner new owner account673 #[solidity(rename_selector = "changeCollectionOwner")]674 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {675 self.consume_store_writes(1)?;676677 let caller = T::CrossAccountId::from_eth(caller);678 let new_owner = T::CrossAccountId::from_eth(new_owner);679 self.set_owner_internal(caller, new_owner)680 .map_err(dispatch_to_evm::<T>)681 }682683 /// Get collection administrators684 ///685 /// @return Vector of tuples with admins address and his substrate mirror.686 /// If address is canonical then substrate mirror is zero and vice versa.687 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {688 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))689 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))690 .collect();691 Ok(result)692 }693694 /// Changes collection owner to another account695 ///696 /// @dev Owner can be changed only by current owner697 /// @param newOwner new owner cross account698 fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {699 self.consume_store_writes(1)?;700701 let caller = T::CrossAccountId::from_eth(caller);702 let new_owner = new_owner.into_sub_cross_account::<T>()?;703 self.set_owner_internal(caller, new_owner)704 .map_err(dispatch_to_evm::<T>)705 }706}707708/// ### Note709/// Do not forget to add: `self.consume_store_reads(1)?;`710fn check_is_owner_or_admin<T: Config>(711 caller: caller,712 collection: &CollectionHandle<T>,713) -> Result<T::CrossAccountId> {714 let caller = T::CrossAccountId::from_eth(caller);715 collection716 .check_is_owner_or_admin(&caller)717 .map_err(dispatch_to_evm::<T>)?;718 Ok(caller)719}720721/// ### Note722/// Do not forget to add: `self.consume_store_writes(1)?;`723fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {724 collection725 .check_is_internal()726 .map_err(dispatch_to_evm::<T>)?;727 collection.save().map_err(dispatch_to_evm::<T>)?;728 Ok(())729}730731/// Contains static property keys and values.732pub mod static_property {733 use evm_coder::{734 execution::{Result, Error},735 };736 use alloc::format;737738 const EXPECT_CONVERT_ERROR: &str = "length < limit";739740 /// Keys.741 pub mod key {742 use super::*;743744 /// Key "baseURI".745 pub fn base_uri() -> up_data_structs::PropertyKey {746 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)747 }748749 /// Key "url".750 pub fn url() -> up_data_structs::PropertyKey {751 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)752 }753754 /// Key "suffix".755 pub fn suffix() -> up_data_structs::PropertyKey {756 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)757 }758759 /// Key "parentNft".760 pub fn parent_nft() -> up_data_structs::PropertyKey {761 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)762 }763 }764765 /// Convert `byte` to [`PropertyKey`].766 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {767 bytes.to_vec().try_into().map_err(|_| {768 Error::Revert(format!(769 "Property key is too long. Max length is {}.",770 up_data_structs::PropertyKey::bound()771 ))772 })773 }774775 /// Convert `bytes` to [`PropertyValue`].776 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {777 bytes.to_vec().try_into().map_err(|_| {778 Error::Revert(format!(779 "Property key is too long. Max length is {}.",780 up_data_structs::PropertyKey::bound()781 ))782 })783 }784}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 custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},25 make_signature,26};27pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};28use pallet_evm_coder_substrate::dispatch_to_evm;29use sp_std::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::{convert_cross_account_to_uint256, convert_tuple_to_cross_account},39 weights::WeightInfo,40};4142/// Events for ethereum collection helper.43#[derive(ToLog)]44pub enum CollectionHelpersEvents {45 /// The collection has been created.46 CollectionCreated {47 /// Collection owner.48 #[indexed]49 owner: address,5051 /// Collection ID.52 #[indexed]53 collection_id: address,54 },55 /// The collection has been destroyed.56 CollectionDestroyed {57 /// Collection ID.58 #[indexed]59 collection_id: address,60 },61}6263/// Does not always represent a full collection, for RFT it is either64/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).65pub trait CommonEvmHandler {66 const CODE: &'static [u8];6768 /// Call precompiled handle.69 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;70}7172/// @title A contract that allows you to work with collections.73#[solidity_interface(name = Collection)]74impl<T: Config> CollectionHandle<T>75where76 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,77{78 /// Set collection property.79 ///80 /// @param key Property key.81 /// @param value Propery value.82 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]83 fn set_collection_property(84 &mut self,85 caller: caller,86 key: string,87 value: bytes,88 ) -> Result<void> {89 let caller = T::CrossAccountId::from_eth(caller);90 let key = <Vec<u8>>::from(key)91 .try_into()92 .map_err(|_| "key too large")?;93 let value = value.0.try_into().map_err(|_| "value too large")?;9495 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })96 .map_err(dispatch_to_evm::<T>)97 }9899 /// Set collection properties.100 ///101 /// @param properties Vector of properties key/value pair.102 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]103 fn set_collection_properties(104 &mut self,105 caller: caller,106 properties: Vec<(string, bytes)>,107 ) -> Result<void> {108 let caller = T::CrossAccountId::from_eth(caller);109110 let properties = properties111 .into_iter()112 .map(|(key, value)| {113 let key = <Vec<u8>>::from(key)114 .try_into()115 .map_err(|_| "key too large")?;116117 let value = value.0.try_into().map_err(|_| "value too large")?;118119 Ok(Property { key, value })120 })121 .collect::<Result<Vec<_>>>()?;122123 <Pallet<T>>::set_collection_properties(self, &caller, properties)124 .map_err(dispatch_to_evm::<T>)125 }126127 /// Delete collection property.128 ///129 /// @param key Property key.130 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]131 fn delete_collection_property(&mut self, caller: caller, key: string) -> Result<()> {132 let caller = T::CrossAccountId::from_eth(caller);133 let key = <Vec<u8>>::from(key)134 .try_into()135 .map_err(|_| "key too large")?;136137 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)138 }139140 /// Delete collection properties.141 ///142 /// @param keys Properties keys.143 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]144 fn delete_collection_properties(&mut self, caller: caller, keys: Vec<string>) -> Result<()> {145 let caller = T::CrossAccountId::from_eth(caller);146 let keys = keys147 .into_iter()148 .map(|key| {149 <Vec<u8>>::from(key)150 .try_into()151 .map_err(|_| Error::Revert("key too large".into()))152 })153 .collect::<Result<Vec<_>>>()?;154155 <Pallet<T>>::delete_collection_properties(self, &caller, keys).map_err(dispatch_to_evm::<T>)156 }157158 /// Get collection property.159 ///160 /// @dev Throws error if key not found.161 ///162 /// @param key Property key.163 /// @return bytes The property corresponding to the key.164 fn collection_property(&self, key: string) -> Result<bytes> {165 let key = <Vec<u8>>::from(key)166 .try_into()167 .map_err(|_| "key too large")?;168169 let props = CollectionProperties::<T>::get(self.id);170 let prop = props.get(&key).ok_or("key not found")?;171172 Ok(bytes(prop.to_vec()))173 }174175 /// Get collection properties.176 ///177 /// @param keys Properties keys. Empty keys for all propertyes.178 /// @return Vector of properties key/value pairs.179 fn collection_properties(&self, keys: Vec<string>) -> Result<Vec<(string, bytes)>> {180 let keys = keys181 .into_iter()182 .map(|key| {183 <Vec<u8>>::from(key)184 .try_into()185 .map_err(|_| Error::Revert("key too large".into()))186 })187 .collect::<Result<Vec<_>>>()?;188189 let properties = Pallet::<T>::filter_collection_properties(190 self.id,191 if keys.is_empty() { None } else { Some(keys) },192 )193 .map_err(dispatch_to_evm::<T>)?;194195 let properties = properties196 .into_iter()197 .map(|p| {198 let key =199 string::from_utf8(p.key.into()).map_err(|e| Error::Revert(format!("{}", e)))?;200 let value = bytes(p.value.to_vec());201 Ok((key, value))202 })203 .collect::<Result<Vec<_>>>()?;204 Ok(properties)205 }206207 /// Set the sponsor of the collection.208 ///209 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.210 ///211 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.212 fn set_collection_sponsor(&mut self, caller: caller, sponsor: address) -> Result<void> {213 self.consume_store_reads_and_writes(1, 1)?;214215 check_is_owner_or_admin(caller, self)?;216217 let sponsor = T::CrossAccountId::from_eth(sponsor);218 self.set_sponsor(sponsor.as_sub().clone())219 .map_err(dispatch_to_evm::<T>)?;220 save(self)221 }222223 /// Set the sponsor of the collection.224 ///225 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.226 ///227 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.228 fn set_collection_sponsor_cross(229 &mut self,230 caller: caller,231 sponsor: EthCrossAccount,232 ) -> Result<void> {233 self.consume_store_reads_and_writes(1, 1)?;234235 check_is_owner_or_admin(caller, self)?;236237 let sponsor = sponsor.into_sub_cross_account::<T>()?;238 self.set_sponsor(sponsor.as_sub().clone())239 .map_err(dispatch_to_evm::<T>)?;240 save(self)241 }242243 /// Whether there is a pending sponsor.244 fn has_collection_pending_sponsor(&self) -> Result<bool> {245 Ok(matches!(246 self.collection.sponsorship,247 SponsorshipState::Unconfirmed(_)248 ))249 }250251 /// Collection sponsorship confirmation.252 ///253 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.254 fn confirm_collection_sponsorship(&mut self, caller: caller) -> Result<void> {255 self.consume_store_writes(1)?;256257 let caller = T::CrossAccountId::from_eth(caller);258 if !self259 .confirm_sponsorship(caller.as_sub())260 .map_err(dispatch_to_evm::<T>)?261 {262 return Err("caller is not set as sponsor".into());263 }264 save(self)265 }266267 /// Remove collection sponsor.268 fn remove_collection_sponsor(&mut self, caller: caller) -> Result<void> {269 self.consume_store_reads_and_writes(1, 1)?;270 check_is_owner_or_admin(caller, self)?;271 self.remove_sponsor().map_err(dispatch_to_evm::<T>)?;272 save(self)273 }274275 /// Get current sponsor.276 ///277 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.278 fn collection_sponsor(&self) -> Result<(address, uint256)> {279 let sponsor = match self.collection.sponsorship.sponsor() {280 Some(sponsor) => sponsor,281 None => return Ok(Default::default()),282 };283 let sponsor = T::CrossAccountId::from_sub(sponsor.clone());284 let result: (address, uint256) = if sponsor.is_canonical_substrate() {285 let sponsor = convert_cross_account_to_uint256::<T>(&sponsor);286 (Default::default(), sponsor)287 } else {288 let sponsor = *sponsor.as_eth();289 (sponsor, Default::default())290 };291 Ok(result)292 }293294 /// Set limits for the collection.295 /// @dev Throws error if limit not found.296 /// @param limit Name of the limit. Valid names:297 /// "accountTokenOwnershipLimit",298 /// "sponsoredDataSize",299 /// "sponsoredDataRateLimit",300 /// "tokenLimit",301 /// "sponsorTransferTimeout",302 /// "sponsorApproveTimeout"303 /// @param value Value of the limit.304 #[solidity(rename_selector = "setCollectionLimit")]305 fn set_int_limit(&mut self, caller: caller, limit: string, value: uint32) -> Result<void> {306 self.consume_store_reads_and_writes(1, 1)?;307308 check_is_owner_or_admin(caller, self)?;309 let mut limits = self.limits.clone();310311 match limit.as_str() {312 "accountTokenOwnershipLimit" => {313 limits.account_token_ownership_limit = Some(value);314 }315 "sponsoredDataSize" => {316 limits.sponsored_data_size = Some(value);317 }318 "sponsoredDataRateLimit" => {319 limits.sponsored_data_rate_limit = Some(SponsoringRateLimit::Blocks(value));320 }321 "tokenLimit" => {322 limits.token_limit = Some(value);323 }324 "sponsorTransferTimeout" => {325 limits.sponsor_transfer_timeout = Some(value);326 }327 "sponsorApproveTimeout" => {328 limits.sponsor_approve_timeout = Some(value);329 }330 _ => {331 return Err(Error::Revert(format!(332 "unknown integer limit \"{}\"",333 limit334 )))335 }336 }337 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)338 .map_err(dispatch_to_evm::<T>)?;339 save(self)340 }341342 /// Set limits for the collection.343 /// @dev Throws error if limit not found.344 /// @param limit Name of the limit. Valid names:345 /// "ownerCanTransfer",346 /// "ownerCanDestroy",347 /// "transfersEnabled"348 /// @param value Value of the limit.349 #[solidity(rename_selector = "setCollectionLimit")]350 fn set_bool_limit(&mut self, caller: caller, limit: string, value: bool) -> Result<void> {351 self.consume_store_reads_and_writes(1, 1)?;352353 check_is_owner_or_admin(caller, self)?;354 let mut limits = self.limits.clone();355356 match limit.as_str() {357 "ownerCanTransfer" => {358 limits.owner_can_transfer = Some(value);359 }360 "ownerCanDestroy" => {361 limits.owner_can_destroy = Some(value);362 }363 "transfersEnabled" => {364 limits.transfers_enabled = Some(value);365 }366 _ => {367 return Err(Error::Revert(format!(368 "unknown boolean limit \"{}\"",369 limit370 )))371 }372 }373 self.limits = <Pallet<T>>::clamp_limits(self.mode.clone(), &self.limits, limits)374 .map_err(dispatch_to_evm::<T>)?;375 save(self)376 }377378 /// Get contract address.379 fn contract_address(&self) -> Result<address> {380 Ok(crate::eth::collection_id_to_address(self.id))381 }382383 /// Add collection admin.384 /// @param newAdmin Cross account administrator address.385 fn add_collection_admin_cross(386 &mut self,387 caller: caller,388 new_admin: EthCrossAccount,389 ) -> Result<void> {390 self.consume_store_writes(2)?;391392 let caller = T::CrossAccountId::from_eth(caller);393 let new_admin = new_admin.into_sub_cross_account::<T>()?;394 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;395 Ok(())396 }397398 /// Remove collection admin.399 /// @param admin Cross account administrator address.400 fn remove_collection_admin_cross(401 &mut self,402 caller: caller,403 admin: EthCrossAccount,404 ) -> Result<void> {405 self.consume_store_writes(2)?;406407 let caller = T::CrossAccountId::from_eth(caller);408 let admin = admin.into_sub_cross_account::<T>()?;409 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;410 Ok(())411 }412413 /// Add collection admin.414 /// @param newAdmin Address of the added administrator.415 fn add_collection_admin(&mut self, caller: caller, new_admin: address) -> Result<void> {416 self.consume_store_writes(2)?;417418 let caller = T::CrossAccountId::from_eth(caller);419 let new_admin = T::CrossAccountId::from_eth(new_admin);420 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;421 Ok(())422 }423424 /// Remove collection admin.425 ///426 /// @param admin Address of the removed administrator.427 fn remove_collection_admin(&mut self, caller: caller, admin: address) -> Result<void> {428 self.consume_store_writes(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 check_is_owner_or_admin(caller, self)?;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 self.collection.permissions = <Pallet<T>>::clamp_permissions(452 self.collection.mode.clone(),453 &self.collection.permissions,454 permissions,455 )456 .map_err(dispatch_to_evm::<T>)?;457458 save(self)459 }460461 /// Toggle accessibility of collection nesting.462 ///463 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'464 /// @param collections Addresses of collections that will be available for nesting.465 #[solidity(rename_selector = "setCollectionNesting")]466 fn set_nesting(467 &mut self,468 caller: caller,469 enable: bool,470 collections: Vec<address>,471 ) -> Result<void> {472 self.consume_store_reads_and_writes(1, 1)?;473474 if collections.is_empty() {475 return Err("no addresses provided".into());476 }477 check_is_owner_or_admin(caller, self)?;478479 let mut permissions = self.collection.permissions.clone();480 match enable {481 false => {482 let mut nesting = permissions.nesting().clone();483 nesting.token_owner = false;484 nesting.restricted = None;485 permissions.nesting = Some(nesting);486 }487 true => {488 let mut bv = OwnerRestrictedSet::new();489 for i in collections {490 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {491 Error::Revert("Can't convert address into collection id".into())492 })?)493 .map_err(|_| "too many collections")?;494 }495 let mut nesting = permissions.nesting().clone();496 nesting.token_owner = true;497 nesting.restricted = Some(bv);498 permissions.nesting = Some(nesting);499 }500 };501502 self.collection.permissions = <Pallet<T>>::clamp_permissions(503 self.collection.mode.clone(),504 &self.collection.permissions,505 permissions,506 )507 .map_err(dispatch_to_evm::<T>)?;508509 save(self)510 }511512 /// Set the collection access method.513 /// @param mode Access mode514 /// 0 for Normal515 /// 1 for AllowList516 fn set_collection_access(&mut self, caller: caller, mode: uint8) -> Result<void> {517 self.consume_store_reads_and_writes(1, 1)?;518519 check_is_owner_or_admin(caller, self)?;520 let permissions = CollectionPermissions {521 access: Some(match mode {522 0 => AccessMode::Normal,523 1 => AccessMode::AllowList,524 _ => return Err("not supported access mode".into()),525 }),526 ..Default::default()527 };528 self.collection.permissions = <Pallet<T>>::clamp_permissions(529 self.collection.mode.clone(),530 &self.collection.permissions,531 permissions,532 )533 .map_err(dispatch_to_evm::<T>)?;534535 save(self)536 }537538 /// Checks that user allowed to operate with collection.539 ///540 /// @param user User address to check.541 fn allowed(&self, user: address) -> Result<bool> {542 Ok(Pallet::<T>::allowed(543 self.id,544 T::CrossAccountId::from_eth(user),545 ))546 }547548 /// Add the user to the allowed list.549 ///550 /// @param user Address of a trusted user.551 fn add_to_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {552 self.consume_store_writes(1)?;553554 let caller = T::CrossAccountId::from_eth(caller);555 let user = T::CrossAccountId::from_eth(user);556 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;557 Ok(())558 }559560 /// Add user to allowed list.561 ///562 /// @param user User cross account address.563 fn add_to_collection_allow_list_cross(564 &mut self,565 caller: caller,566 user: EthCrossAccount,567 ) -> Result<void> {568 self.consume_store_writes(1)?;569570 let caller = T::CrossAccountId::from_eth(caller);571 let user = user.into_sub_cross_account::<T>()?;572 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;573 Ok(())574 }575576 /// Remove the user from the allowed list.577 ///578 /// @param user Address of a removed user.579 fn remove_from_collection_allow_list(&mut self, caller: caller, user: address) -> Result<void> {580 self.consume_store_writes(1)?;581582 let caller = T::CrossAccountId::from_eth(caller);583 let user = T::CrossAccountId::from_eth(user);584 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;585 Ok(())586 }587588 /// Remove user from allowed list.589 ///590 /// @param user User cross account address.591 fn remove_from_collection_allow_list_cross(592 &mut self,593 caller: caller,594 user: EthCrossAccount,595 ) -> Result<void> {596 self.consume_store_writes(1)?;597598 let caller = T::CrossAccountId::from_eth(caller);599 let user = user.into_sub_cross_account::<T>()?;600 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;601 Ok(())602 }603604 /// Switch permission for minting.605 ///606 /// @param mode Enable if "true".607 fn set_collection_mint_mode(&mut self, caller: caller, mode: bool) -> Result<void> {608 self.consume_store_reads_and_writes(1, 1)?;609610 check_is_owner_or_admin(caller, self)?;611 let permissions = CollectionPermissions {612 mint_mode: Some(mode),613 ..Default::default()614 };615 self.collection.permissions = <Pallet<T>>::clamp_permissions(616 self.collection.mode.clone(),617 &self.collection.permissions,618 permissions,619 )620 .map_err(dispatch_to_evm::<T>)?;621622 save(self)623 }624625 /// Check that account is the owner or admin of the collection626 ///627 /// @param user account to verify628 /// @return "true" if account is the owner or admin629 #[solidity(rename_selector = "isOwnerOrAdmin")]630 fn is_owner_or_admin_eth(&self, user: address) -> Result<bool> {631 let user = T::CrossAccountId::from_eth(user);632 Ok(self.is_owner_or_admin(&user))633 }634635 /// Check that account is the owner or admin of the collection636 ///637 /// @param user User cross account to verify638 /// @return "true" if account is the owner or admin639 fn is_owner_or_admin_cross(&self, user: EthCrossAccount) -> Result<bool> {640 let user = user.into_sub_cross_account::<T>()?;641 Ok(self.is_owner_or_admin(&user))642 }643644 /// Returns collection type645 ///646 /// @return `Fungible` or `NFT` or `ReFungible`647 fn unique_collection_type(&self) -> Result<string> {648 let mode = match self.collection.mode {649 CollectionMode::Fungible(_) => "Fungible",650 CollectionMode::NFT => "NFT",651 CollectionMode::ReFungible => "ReFungible",652 };653 Ok(mode.into())654 }655656 /// Get collection owner.657 ///658 /// @return Tuble with sponsor address and his substrate mirror.659 /// If address is canonical then substrate mirror is zero and vice versa.660 fn collection_owner(&self) -> Result<EthCrossAccount> {661 Ok(EthCrossAccount::from_sub_cross_account::<T>(662 &T::CrossAccountId::from_sub(self.owner.clone()),663 ))664 }665666 /// Changes collection owner to another account667 ///668 /// @dev Owner can be changed only by current owner669 /// @param newOwner new owner account670 #[solidity(rename_selector = "changeCollectionOwner")]671 fn set_owner(&mut self, caller: caller, new_owner: address) -> Result<void> {672 self.consume_store_writes(1)?;673674 let caller = T::CrossAccountId::from_eth(caller);675 let new_owner = T::CrossAccountId::from_eth(new_owner);676 self.set_owner_internal(caller, new_owner)677 .map_err(dispatch_to_evm::<T>)678 }679680 /// Get collection administrators681 ///682 /// @return Vector of tuples with admins address and his substrate mirror.683 /// If address is canonical then substrate mirror is zero and vice versa.684 fn collection_admins(&self) -> Result<Vec<EthCrossAccount>> {685 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))686 .map(|(admin, _)| EthCrossAccount::from_sub_cross_account::<T>(&admin))687 .collect();688 Ok(result)689 }690691 /// Changes collection owner to another account692 ///693 /// @dev Owner can be changed only by current owner694 /// @param newOwner new owner cross account695 fn set_owner_cross(&mut self, caller: caller, new_owner: EthCrossAccount) -> Result<void> {696 self.consume_store_writes(1)?;697698 let caller = T::CrossAccountId::from_eth(caller);699 let new_owner = new_owner.into_sub_cross_account::<T>()?;700 self.set_owner_internal(caller, new_owner)701 .map_err(dispatch_to_evm::<T>)702 }703}704705/// ### Note706/// Do not forget to add: `self.consume_store_reads(1)?;`707fn check_is_owner_or_admin<T: Config>(708 caller: caller,709 collection: &CollectionHandle<T>,710) -> Result<T::CrossAccountId> {711 let caller = T::CrossAccountId::from_eth(caller);712 collection713 .check_is_owner_or_admin(&caller)714 .map_err(dispatch_to_evm::<T>)?;715 Ok(caller)716}717718/// ### Note719/// Do not forget to add: `self.consume_store_writes(1)?;`720fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {721 collection722 .check_is_internal()723 .map_err(dispatch_to_evm::<T>)?;724 collection.save().map_err(dispatch_to_evm::<T>)?;725 Ok(())726}727728/// Contains static property keys and values.729pub mod static_property {730 use evm_coder::{731 execution::{Result, Error},732 };733 use alloc::format;734735 const EXPECT_CONVERT_ERROR: &str = "length < limit";736737 /// Keys.738 pub mod key {739 use super::*;740741 /// Key "baseURI".742 pub fn base_uri() -> up_data_structs::PropertyKey {743 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)744 }745746 /// Key "url".747 pub fn url() -> up_data_structs::PropertyKey {748 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)749 }750751 /// Key "suffix".752 pub fn suffix() -> up_data_structs::PropertyKey {753 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)754 }755756 /// Key "parentNft".757 pub fn parent_nft() -> up_data_structs::PropertyKey {758 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)759 }760 }761762 /// Convert `byte` to [`PropertyKey`].763 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {764 bytes.to_vec().try_into().map_err(|_| {765 Error::Revert(format!(766 "Property key is too long. Max length is {}.",767 up_data_structs::PropertyKey::bound()768 ))769 })770 }771772 /// Convert `bytes` to [`PropertyValue`].773 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {774 bytes.to_vec().try_into().map_err(|_| {775 Error::Revert(format!(776 "Property key is too long. Max length is {}.",777 up_data_structs::PropertyKey::bound()778 ))779 })780 }781}pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -17,7 +17,6 @@
//! Implementation of magic contract
extern crate alloc;
-use alloc::string::ToString;
use core::marker::PhantomData;
use evm_coder::{
abi::AbiWriter,
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -28,7 +28,6 @@
custom_signature::{SignatureUnit, FunctionSignature, SignaturePreferences},
make_signature,
};
-use pallet_common::eth::convert_tuple_to_cross_account;
use up_data_structs::CollectionMode;
use pallet_common::erc::{CommonEvmHandler, PrecompileResult};
use sp_std::vec::Vec;
pallets/refungible/src/erc.rsdiffbeforeafterboth--- a/pallets/refungible/src/erc.rs
+++ b/pallets/refungible/src/erc.rs
@@ -21,7 +21,6 @@
extern crate alloc;
-use alloc::string::ToString;
use core::{
char::{REPLACEMENT_CHARACTER, decode_utf16},
convert::TryInto,
@@ -39,7 +38,6 @@
use pallet_common::{
CollectionHandle, CollectionPropertyPermissions,
erc::{CommonEvmHandler, CollectionCall, static_property::key},
- eth::convert_tuple_to_cross_account,
};
use pallet_evm::{account::CrossAccountId, PrecompileHandle};
use pallet_evm_coder_substrate::{call, dispatch_to_evm};
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -42,10 +42,7 @@
CreateCollectionData,
};
-use crate::{
- weights::WeightInfo, Config, SelfWeightOf, NftTransferBasket, FungibleTransferBasket,
- ReFungibleTransferBasket, NftApproveBasket, FungibleApproveBasket, RefungibleApproveBasket,
-};
+use crate::{weights::WeightInfo, Config, SelfWeightOf};
use alloc::format;
use sp_std::vec::Vec;
runtime/common/config/pallets/scheduler.rsdiffbeforeafterboth--- a/runtime/common/config/pallets/scheduler.rs
+++ b/runtime/common/config/pallets/scheduler.rs
@@ -25,7 +25,7 @@
use codec::Decode;
use crate::{
runtime_common::{scheduler::SchedulerPaymentExecutor, config::substrate::RuntimeBlockWeights},
- Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, OriginCaller, Balances,
+ Runtime, RuntimeCall, RuntimeEvent, RuntimeOrigin, OriginCaller,
};
use pallet_unique_scheduler_v2::ScheduledEnsureOriginSuccess;
use up_common::types::AccountId;
runtime/common/scheduler.rsdiffbeforeafterboth--- a/runtime/common/scheduler.rs
+++ b/runtime/common/scheduler.rs
@@ -14,19 +14,16 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-use frame_support::{
- traits::NamedReservableCurrency,
- dispatch::{GetDispatchInfo, PostDispatchInfo, DispatchInfo},
-};
+use frame_support::dispatch::{GetDispatchInfo, PostDispatchInfo, DispatchInfo};
use sp_runtime::{
traits::{Dispatchable, Applyable, Member},
generic::Era,
transaction_validity::TransactionValidityError,
- DispatchErrorWithPostInfo, DispatchError,
+ DispatchErrorWithPostInfo,
};
use codec::Encode;
-use crate::{Runtime, RuntimeCall, RuntimeOrigin, Balances};
-use up_common::types::{AccountId, Balance};
+use crate::{Runtime, RuntimeCall, RuntimeOrigin};
+use up_common::types::AccountId;
use fp_self_contained::SelfContainedCall;
use pallet_unique_scheduler_v2::DispatchCall;
use pallet_transaction_payment::ChargeTransactionPayment;