difftreelog
fix PR comments
in: master
8 files changed
pallets/balances-adapter/src/common.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/common.rs
+++ b/pallets/balances-adapter/src/common.rs
@@ -7,6 +7,8 @@
use up_data_structs::TokenId;
pub struct CommonWeights<T: Config>(PhantomData<T>);
+
+// All implementations with `Weight::default` used in methods that return error `UnsupportedOperation`.
impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_multiple_items(_amount: &[up_data_structs::CreateItemData]) -> Weight {
Weight::default()
pallets/balances-adapter/src/erc.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/erc.rs
+++ b/pallets/balances-adapter/src/erc.rs
@@ -1,9 +1,8 @@
use crate::{Config, NativeFungibleHandle, Pallet, SelfWeightOf};
-use evm_coder::{abi::AbiType, ToLog, generate_stubgen, solidity_interface, types::*};
-use frame_support::traits::{Currency, ExistenceRequirement};
+use evm_coder::{abi::AbiType, generate_stubgen, solidity_interface, types::*};
+use frame_support::traits::{Currency};
use pallet_balances::WeightInfo;
use pallet_common::{
- consume_store_reads,
erc::{CommonEvmHandler, CrossAccountId, PrecompileHandle, PrecompileResult},
eth::CrossAddress,
};
@@ -14,38 +13,24 @@
};
use pallet_structure::{SelfWeightOf as StructureWeight, weights::WeightInfo as _};
use sp_core::{U256, Get};
-use sp_std::vec::Vec;
frontier_contract! {
macro_rules! NativeFungibleHandle_result {...}
impl<T: Config> Contract for NativeFungibleHandle<T> {...}
}
-#[derive(ToLog)]
-pub enum ERC20Events {
- Transfer {
- #[indexed]
- from: Address,
- #[indexed]
- to: Address,
- value: U256,
- },
-}
-
-#[solidity_interface(name = ERC20, events(ERC20Events), enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]
+#[solidity_interface(name = ERC20, enum(derive(PreDispatch)), enum_attr(weight), expect_selector = 0x942e8b22)]
impl<T: Config> NativeFungibleHandle<T> {
fn allowance(&self, _owner: Address, _spender: Address) -> Result<U256> {
Ok(U256::zero())
}
- // #[weight(<SelfWeightOf<T>>::approve())]
fn approve(&mut self, _caller: Caller, _spender: Address, _amount: U256) -> Result<bool> {
- // self.consume_store_reads(1)?;
Err("Approve not supported".into())
}
fn balance_of(&self, owner: Address) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let owner = T::CrossAccountId::from_eth(owner);
let balance = <T as Config>::Currency::free_balance(owner.as_sub());
Ok(balance.into())
@@ -64,7 +49,7 @@
}
fn total_supply(&self) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let total = <T as Config>::Currency::total_issuance();
Ok(total.into())
}
@@ -111,7 +96,7 @@
T::AccountId: From<[u8; 32]>,
{
fn balance_of_cross(&self, owner: CrossAddress) -> Result<U256> {
- consume_store_reads(self, 1)?;
+ self.consume_store_reads(1)?;
let owner = owner.into_sub_cross_account::<T>()?;
let balance = <T as Config>::Currency::free_balance(owner.as_sub());
Ok(balance.into())
@@ -122,18 +107,13 @@
let caller = T::CrossAccountId::from_eth(caller);
let to = to.into_sub_cross_account::<T>()?;
let amount = amount.try_into().map_err(|_| "amount overflow")?;
- // let budget = self
- // .recorder
- // .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ let budget = self
+ .recorder()
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer(self, &caller, &to, amount, &budget)
+ .map_err(|e| dispatch_to_evm::<T>(e.error))?;
- // <Pallet<T>>::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?;
- <T as Config>::Currency::transfer(
- caller.as_sub(),
- to.as_sub(),
- amount,
- ExistenceRequirement::KeepAlive,
- )
- .map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
@@ -154,19 +134,13 @@
return Err("no permission".into());
}
- // let budget = self
- // .recorder
- // .weight_calls_budget(<StructureWeight<T>>::find_parent());
+ let budget = self
+ .recorder()
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
+
+ <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)
+ .map_err(|e| dispatch_to_evm::<T>(e.error))?;
- // <Pallet<T>>::transfer_from(self, &caller, &from, &to, amount, &budget)
- // .map_err(dispatch_to_evm::<T>)?;
- <T as Config>::Currency::transfer(
- caller.as_sub(),
- to.as_sub(),
- amount,
- ExistenceRequirement::KeepAlive,
- )
- .map_err(dispatch_to_evm::<T>)?;
Ok(true)
}
}
pallets/balances-adapter/src/lib.rsdiffbeforeafterboth--- a/pallets/balances-adapter/src/lib.rs
+++ b/pallets/balances-adapter/src/lib.rs
@@ -1,4 +1,3 @@
-// #![doc = include_str!("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
@@ -12,9 +11,6 @@
pub mod erc;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
-
-const NATIVE_FUNGIBLE_COLLECTION_ID: up_data_structs::CollectionId =
- up_data_structs::CollectionId(0);
/// Handle for native fungible collection
pub struct NativeFungibleHandle<T: Config>(SubstrateRecorder<T>);
@@ -57,7 +53,10 @@
traits::{Currency, ExistenceRequirement, Get},
};
use pallet_balances::WeightInfo;
- use pallet_common::{erc::CrossAccountId, Error as CommonError, Pallet as PalletCommon};
+ use pallet_common::{
+ erc::CrossAccountId, Error as CommonError, Pallet as PalletCommon,
+ NATIVE_FUNGIBLE_COLLECTION_ID,
+ };
use pallet_structure::Pallet as PalletStructure;
use sp_core::U256;
use sp_runtime::DispatchError;
@@ -95,10 +94,9 @@
/// Checks if a non-owner has (enough) allowance from the owner to perform operations on the tokens.
/// Returns the expected remaining allowance - it should be set manually if the transaction proceeds.
///
- /// - `collection`: Collection that contains the token.
/// - `spender`: CrossAccountId who has the allowance rights.
/// - `from`: The owner of the tokens who sets the allowance.
- /// - `amount`: Amount of tokens by which the allowance sholud be reduced.
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
fn check_allowed(
spender: &T::CrossAccountId,
from: &T::CrossAccountId,
@@ -127,10 +125,11 @@
/// Transfers the specified amount of tokens. Will check that
/// the transfer is allowed for the token.
///
+ /// - `collection`: Collection that contains the token.
/// - `from`: Owner of tokens to transfer.
/// - `to`: Recepient of transfered tokens.
/// - `amount`: Amount of tokens to transfer.
- /// - `collection`: Collection that contains the token
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
pub fn transfer(
_collection: &NativeFungibleHandle<T>,
from: &T::CrossAccountId,
@@ -147,7 +146,7 @@
amount
.try_into()
.map_err(|_| sp_runtime::ArithmeticError::Overflow)?,
- ExistenceRequirement::KeepAlive,
+ ExistenceRequirement::AllowDeath,
)?;
<PalletStructure<T>>::nest_if_sent_to_token(
@@ -175,6 +174,17 @@
})
}
+ /// Transfer NFT token from one account to another.
+ ///
+ /// Same as the [`Self::transfer`] but spender doesn't needs to be the owner of the token.
+ /// The owner should set allowance for the spender to transfer token.
+ ///
+ /// - `collection`: Collection that contains the token.
+ /// - `spender`: Account that spend the money.
+ /// - `from`: Owner of tokens to transfer.
+ /// - `to`: Recepient of transfered tokens.
+ /// - `amount`: Amount of tokens to transfer.
+ /// - `nesting_budget`: Limit for searching parents in-depth to check ownership.
pub fn transfer_from(
collection: &NativeFungibleHandle<T>,
spender: &T::CrossAccountId,
pallets/common/src/erc.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};20use pallet_evm_coder_substrate::{21 abi::AbiType,22 solidity_interface, ToLog,23 types::*,24 execution::{Result, Error, PreDispatch},25 frontier_contract,26};27use pallet_evm_coder_substrate::dispatch_to_evm;28use sp_std::{vec, vec::Vec};29use up_data_structs::{30 CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property, SponsoringRateLimit,31 SponsorshipState,32};3334use crate::{35 Pallet, CollectionHandle, Config, CollectionProperties, eth, SelfWeightOf, weights::WeightInfo,36};3738frontier_contract! {39 macro_rules! CollectionHandle_result {...}40 impl<T: Config> Contract for CollectionHandle<T> {...}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 /// The collection has been changed.63 CollectionChanged {64 /// Collection ID.65 #[indexed]66 collection_id: Address,67 },68}6970/// Does not always represent a full collection, for RFT it is either71/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).72pub trait CommonEvmHandler {73 /// Raw compiled binary code of the contract stub74 const CODE: &'static [u8];7576 /// Call precompiled handle.77 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;78}7980impl CommonEvmHandler for () {81 const CODE: &'static [u8] = &[];8283 fn call(self, _handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {84 None85 }86}8788/// @title A contract that allows you to work with collections.89#[solidity_interface(name = Collection, enum(derive(PreDispatch)), enum_attr(weight))]90impl<T: Config> CollectionHandle<T>91where92 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,93{94 /// Set collection property.95 ///96 /// @param key Property key.97 /// @param value Propery value.98 #[solidity(hide)]99 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]100 fn set_collection_property(&mut self, caller: Caller, key: String, value: Bytes) -> Result<()> {101 let caller = T::CrossAccountId::from_eth(caller);102 let key = <Vec<u8>>::from(key)103 .try_into()104 .map_err(|_| "key too large")?;105 let value = value.0.try_into().map_err(|_| "value too large")?;106107 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })108 .map_err(dispatch_to_evm::<T>)109 }110111 /// Set collection properties.112 ///113 /// @param properties Vector of properties key/value pair.114 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]115 fn set_collection_properties(116 &mut self,117 caller: Caller,118 properties: Vec<eth::Property>,119 ) -> Result<()> {120 let caller = T::CrossAccountId::from_eth(caller);121122 let properties = properties123 .into_iter()124 .map(eth::Property::try_into)125 .collect::<Result<Vec<_>>>()?;126127 <Pallet<T>>::set_collection_properties(self, &caller, properties.into_iter())128 .map_err(dispatch_to_evm::<T>)129 }130131 /// Delete collection property.132 ///133 /// @param key Property key.134 #[solidity(hide)]135 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]136 fn delete_collection_property(&mut self, caller: Caller, key: String) -> Result<()> {137 let caller = T::CrossAccountId::from_eth(caller);138 let key = <Vec<u8>>::from(key)139 .try_into()140 .map_err(|_| "key too large")?;141142 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)143 }144145 /// Delete collection properties.146 ///147 /// @param keys Properties keys.148 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]149 fn delete_collection_properties(&mut self, caller: Caller, keys: Vec<String>) -> Result<()> {150 let caller = T::CrossAccountId::from_eth(caller);151 let keys = keys152 .into_iter()153 .map(|key| {154 <Vec<u8>>::from(key)155 .try_into()156 .map_err(|_| Error::Revert("key too large".into()))157 })158 .collect::<Result<Vec<_>>>()?;159160 <Pallet<T>>::delete_collection_properties(self, &caller, keys.into_iter())161 .map_err(dispatch_to_evm::<T>)162 }163164 /// Get collection property.165 ///166 /// @dev Throws error if key not found.167 ///168 /// @param key Property key.169 /// @return bytes The property corresponding to the key.170 fn collection_property(&self, key: String) -> Result<Bytes> {171 let key = <Vec<u8>>::from(key)172 .try_into()173 .map_err(|_| "key too large")?;174175 let props = CollectionProperties::<T>::get(self.id);176 let prop = props.get(&key).ok_or("key not found")?;177178 Ok(Bytes(prop.to_vec()))179 }180181 /// Get collection properties.182 ///183 /// @param keys Properties keys. Empty keys for all propertyes.184 /// @return Vector of properties key/value pairs.185 fn collection_properties(&self, keys: Vec<String>) -> Result<Vec<eth::Property>> {186 let keys = keys187 .into_iter()188 .map(|key| {189 <Vec<u8>>::from(key)190 .try_into()191 .map_err(|_| Error::Revert("key too large".into()))192 })193 .collect::<Result<Vec<_>>>()?;194195 let properties = Pallet::<T>::filter_collection_properties(196 self.id,197 if keys.is_empty() { None } else { Some(keys) },198 )199 .map_err(dispatch_to_evm::<T>)?;200201 let properties = properties202 .into_iter()203 .map(Property::try_into)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 #[solidity(hide)]214 fn set_collection_sponsor(&mut self, caller: Caller, sponsor: Address) -> Result<()> {215 self.consume_store_reads_and_writes(1, 1)?;216217 let caller = T::CrossAccountId::from_eth(caller);218219 let sponsor = T::CrossAccountId::from_eth(sponsor);220 self.set_sponsor(&caller, sponsor.as_sub().clone())221 .map_err(dispatch_to_evm::<T>)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: eth::CrossAddress,233 ) -> Result<()> {234 self.consume_store_reads_and_writes(1, 1)?;235236 let caller = T::CrossAccountId::from_eth(caller);237238 let sponsor = sponsor.into_sub_cross_account::<T>()?;239 self.set_sponsor(&caller, sponsor.as_sub().clone())240 .map_err(dispatch_to_evm::<T>)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<()> {255 self.consume_store_writes(1)?;256257 let caller = T::CrossAccountId::from_eth(caller);258 self.confirm_sponsorship(caller.as_sub())259 .map_err(dispatch_to_evm::<T>)260 }261262 /// Remove collection sponsor.263 fn remove_collection_sponsor(&mut self, caller: Caller) -> Result<()> {264 self.consume_store_reads_and_writes(1, 1)?;265 let caller = T::CrossAccountId::from_eth(caller);266 self.remove_sponsor(&caller).map_err(dispatch_to_evm::<T>)267 }268269 /// Get current sponsor.270 ///271 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.272 fn collection_sponsor(&self) -> Result<eth::CrossAddress> {273 let sponsor = match self.collection.sponsorship.sponsor() {274 Some(sponsor) => sponsor,275 None => return Ok(Default::default()),276 };277278 Ok(eth::CrossAddress::from_sub::<T>(sponsor))279 }280281 /// Get current collection limits.282 ///283 /// @return Array of collection limits284 fn collection_limits(&self) -> Result<Vec<eth::CollectionLimit>> {285 let limits = &self.collection.limits;286287 Ok(vec![288 eth::CollectionLimit::new(289 eth::CollectionLimitField::AccountTokenOwnership,290 limits.account_token_ownership_limit,291 ),292 eth::CollectionLimit::new(293 eth::CollectionLimitField::SponsoredDataSize,294 limits.sponsored_data_size,295 ),296 limits297 .sponsored_data_rate_limit298 .and_then(|limit| {299 if let SponsoringRateLimit::Blocks(blocks) = limit {300 Some(eth::CollectionLimit::new(301 eth::CollectionLimitField::SponsoredDataRateLimit,302 Some(blocks),303 ))304 } else {305 None306 }307 })308 .unwrap_or_else(|| {309 eth::CollectionLimit::new(310 eth::CollectionLimitField::SponsoredDataRateLimit,311 Default::default(),312 )313 }),314 eth::CollectionLimit::new(eth::CollectionLimitField::TokenLimit, limits.token_limit),315 eth::CollectionLimit::new(316 eth::CollectionLimitField::SponsorTransferTimeout,317 limits.sponsor_transfer_timeout,318 ),319 eth::CollectionLimit::new(320 eth::CollectionLimitField::SponsorApproveTimeout,321 limits.sponsor_approve_timeout,322 ),323 eth::CollectionLimit::new(324 eth::CollectionLimitField::OwnerCanTransfer,325 limits.owner_can_transfer.map(u32::from),326 ),327 eth::CollectionLimit::new(328 eth::CollectionLimitField::OwnerCanDestroy,329 limits.owner_can_destroy.map(u32::from),330 ),331 eth::CollectionLimit::new(332 eth::CollectionLimitField::TransferEnabled,333 limits.transfers_enabled.map(u32::from),334 ),335 ])336 }337338 /// Set limits for the collection.339 /// @dev Throws error if limit not found.340 /// @param limit Some limit.341 #[solidity(rename_selector = "setCollectionLimit")]342 fn set_collection_limit(&mut self, caller: Caller, limit: eth::CollectionLimit) -> Result<()> {343 self.consume_store_reads_and_writes(1, 1)?;344345 if !limit.has_value() {346 return Err(Error::Revert("user can't disable limits".into()));347 }348349 let caller = T::CrossAccountId::from_eth(caller);350 <Pallet<T>>::update_limits(&caller, self, limit.try_into()?).map_err(dispatch_to_evm::<T>)351 }352353 /// Get contract address.354 fn contract_address(&self) -> Result<Address> {355 Ok(crate::eth::collection_id_to_address(self.id))356 }357358 /// Add collection admin.359 /// @param newAdmin Cross account administrator address.360 fn add_collection_admin_cross(361 &mut self,362 caller: Caller,363 new_admin: eth::CrossAddress,364 ) -> Result<()> {365 self.consume_store_reads_and_writes(2, 2)?;366367 let caller = T::CrossAccountId::from_eth(caller);368 let new_admin = new_admin.into_sub_cross_account::<T>()?;369 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;370 Ok(())371 }372373 /// Remove collection admin.374 /// @param admin Cross account administrator address.375 fn remove_collection_admin_cross(376 &mut self,377 caller: Caller,378 admin: eth::CrossAddress,379 ) -> Result<()> {380 self.consume_store_reads_and_writes(2, 2)?;381382 let caller = T::CrossAccountId::from_eth(caller);383 let admin = admin.into_sub_cross_account::<T>()?;384 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;385 Ok(())386 }387388 /// Add collection admin.389 /// @param newAdmin Address of the added administrator.390 #[solidity(hide)]391 fn add_collection_admin(&mut self, caller: Caller, new_admin: Address) -> Result<()> {392 self.consume_store_reads_and_writes(2, 2)?;393394 let caller = T::CrossAccountId::from_eth(caller);395 let new_admin = T::CrossAccountId::from_eth(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 ///402 /// @param admin Address of the removed administrator.403 #[solidity(hide)]404 fn remove_collection_admin(&mut self, caller: Caller, admin: Address) -> Result<()> {405 self.consume_store_reads_and_writes(2, 2)?;406407 let caller = T::CrossAccountId::from_eth(caller);408 let admin = T::CrossAccountId::from_eth(admin);409 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;410 Ok(())411 }412413 /// Toggle accessibility of collection nesting.414 ///415 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'416 #[solidity(rename_selector = "setCollectionNesting")]417 fn set_nesting_bool(&mut self, caller: Caller, enable: bool) -> Result<()> {418 self.consume_store_reads_and_writes(1, 1)?;419420 let caller = T::CrossAccountId::from_eth(caller);421422 let mut permissions = self.collection.permissions.clone();423 let mut nesting = permissions.nesting().clone();424 nesting.token_owner = enable;425 nesting.restricted = None;426 permissions.nesting = Some(nesting);427428 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)429 }430431 /// Toggle accessibility of collection nesting.432 ///433 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'434 /// @param collections Addresses of collections that will be available for nesting.435 #[solidity(rename_selector = "setCollectionNesting")]436 fn set_nesting(437 &mut self,438 caller: Caller,439 enable: bool,440 collections: Vec<Address>,441 ) -> Result<()> {442 self.consume_store_reads_and_writes(1, 1)?;443444 if collections.is_empty() {445 return Err("no addresses provided".into());446 }447 let caller = T::CrossAccountId::from_eth(caller);448449 let mut permissions = self.collection.permissions.clone();450 match enable {451 false => {452 let mut nesting = permissions.nesting().clone();453 nesting.token_owner = false;454 nesting.restricted = None;455 permissions.nesting = Some(nesting);456 }457 true => {458 let mut bv = OwnerRestrictedSet::new();459 for i in collections {460 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {461 Error::Revert("Can't convert address into collection id".into())462 })?)463 .map_err(|_| "too many collections")?;464 }465 let mut nesting = permissions.nesting().clone();466 nesting.token_owner = true;467 nesting.restricted = Some(bv);468 permissions.nesting = Some(nesting);469 }470 };471472 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)473 }474475 /// Returns nesting for a collection476 #[solidity(rename_selector = "collectionNestingRestrictedCollectionIds")]477 fn collection_nesting_restricted_ids(&self) -> Result<eth::CollectionNesting> {478 let nesting = self.collection.permissions.nesting();479480 Ok(eth::CollectionNesting::new(481 nesting.token_owner,482 nesting483 .restricted484 .clone()485 .map(|b| b.0.into_inner().iter().map(|id| id.0.into()).collect())486 .unwrap_or_default(),487 ))488 }489490 /// Returns permissions for a collection491 fn collection_nesting_permissions(&self) -> Result<Vec<eth::CollectionNestingPermission>> {492 let nesting = self.collection.permissions.nesting();493 Ok(vec![494 eth::CollectionNestingPermission::new(495 eth::CollectionPermissionField::CollectionAdmin,496 nesting.collection_admin,497 ),498 eth::CollectionNestingPermission::new(499 eth::CollectionPermissionField::TokenOwner,500 nesting.token_owner,501 ),502 ])503 }504 /// Set the collection access method.505 /// @param mode Access mode506 fn set_collection_access(&mut self, caller: Caller, mode: eth::AccessMode) -> Result<()> {507 self.consume_store_reads_and_writes(1, 1)?;508509 let caller = T::CrossAccountId::from_eth(caller);510 let permissions = CollectionPermissions {511 access: Some(mode.into()),512 ..Default::default()513 };514 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)515 }516517 /// Checks that user allowed to operate with collection.518 ///519 /// @param user User address to check.520 fn allowlisted_cross(&self, user: eth::CrossAddress) -> Result<bool> {521 let user = user.into_sub_cross_account::<T>()?;522 Ok(Pallet::<T>::allowed(self.id, user))523 }524525 /// Add the user to the allowed list.526 ///527 /// @param user Address of a trusted user.528 #[solidity(hide)]529 fn add_to_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {530 self.consume_store_writes(1)?;531532 let caller = T::CrossAccountId::from_eth(caller);533 let user = T::CrossAccountId::from_eth(user);534 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;535 Ok(())536 }537538 /// Add user to allowed list.539 ///540 /// @param user User cross account address.541 fn add_to_collection_allow_list_cross(542 &mut self,543 caller: Caller,544 user: eth::CrossAddress,545 ) -> Result<()> {546 self.consume_store_writes(1)?;547548 let caller = T::CrossAccountId::from_eth(caller);549 let user = user.into_sub_cross_account::<T>()?;550 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;551 Ok(())552 }553554 /// Remove the user from the allowed list.555 ///556 /// @param user Address of a removed user.557 #[solidity(hide)]558 fn remove_from_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {559 self.consume_store_writes(1)?;560561 let caller = T::CrossAccountId::from_eth(caller);562 let user = T::CrossAccountId::from_eth(user);563 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;564 Ok(())565 }566567 /// Remove user from allowed list.568 ///569 /// @param user User cross account address.570 fn remove_from_collection_allow_list_cross(571 &mut self,572 caller: Caller,573 user: eth::CrossAddress,574 ) -> Result<()> {575 self.consume_store_writes(1)?;576577 let caller = T::CrossAccountId::from_eth(caller);578 let user = user.into_sub_cross_account::<T>()?;579 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;580 Ok(())581 }582583 /// Switch permission for minting.584 ///585 /// @param mode Enable if "true".586 fn set_collection_mint_mode(&mut self, caller: Caller, mode: bool) -> Result<()> {587 self.consume_store_reads_and_writes(1, 1)?;588589 let caller = T::CrossAccountId::from_eth(caller);590 let permissions = CollectionPermissions {591 mint_mode: Some(mode),592 ..Default::default()593 };594 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)595 }596597 /// Check that account is the owner or admin of the collection598 ///599 /// @param user account to verify600 /// @return "true" if account is the owner or admin601 #[solidity(hide, rename_selector = "isOwnerOrAdmin")]602 fn is_owner_or_admin_eth(&self, user: Address) -> Result<bool> {603 let user = T::CrossAccountId::from_eth(user);604 Ok(self.is_owner_or_admin(&user))605 }606607 /// Check that account is the owner or admin of the collection608 ///609 /// @param user User cross account to verify610 /// @return "true" if account is the owner or admin611 fn is_owner_or_admin_cross(&self, user: eth::CrossAddress) -> Result<bool> {612 let user = user.into_sub_cross_account::<T>()?;613 Ok(self.is_owner_or_admin(&user))614 }615616 /// Returns collection type617 ///618 /// @return `Fungible` or `NFT` or `ReFungible`619 fn unique_collection_type(&self) -> Result<String> {620 let mode = match self.collection.mode {621 CollectionMode::Fungible(_) => "Fungible",622 CollectionMode::NFT => "NFT",623 CollectionMode::ReFungible => "ReFungible",624 };625 Ok(mode.into())626 }627628 /// Get collection owner.629 ///630 /// @return Tuble with sponsor address and his substrate mirror.631 /// If address is canonical then substrate mirror is zero and vice versa.632 fn collection_owner(&self) -> Result<eth::CrossAddress> {633 Ok(eth::CrossAddress::from_sub_cross_account::<T>(634 &T::CrossAccountId::from_sub(self.owner.clone()),635 ))636 }637638 /// Changes collection owner to another account639 ///640 /// @dev Owner can be changed only by current owner641 /// @param newOwner new owner account642 #[solidity(hide, rename_selector = "changeCollectionOwner")]643 fn set_owner(&mut self, caller: Caller, new_owner: Address) -> Result<()> {644 self.consume_store_writes(1)?;645646 let caller = T::CrossAccountId::from_eth(caller);647 let new_owner = T::CrossAccountId::from_eth(new_owner);648 self.change_owner(caller, new_owner)649 .map_err(dispatch_to_evm::<T>)650 }651652 /// Get collection administrators653 ///654 /// @return Vector of tuples with admins address and his substrate mirror.655 /// If address is canonical then substrate mirror is zero and vice versa.656 fn collection_admins(&self) -> Result<Vec<eth::CrossAddress>> {657 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))658 .map(|(admin, _)| eth::CrossAddress::from_sub_cross_account::<T>(&admin))659 .collect();660 Ok(result)661 }662663 /// Changes collection owner to another account664 ///665 /// @dev Owner can be changed only by current owner666 /// @param newOwner new owner cross account667 fn change_collection_owner_cross(668 &mut self,669 caller: Caller,670 new_owner: eth::CrossAddress,671 ) -> Result<()> {672 self.consume_store_writes(1)?;673674 let caller = T::CrossAccountId::from_eth(caller);675 let new_owner = new_owner.into_sub_cross_account::<T>()?;676 self.change_owner(caller, new_owner)677 .map_err(dispatch_to_evm::<T>)678 }679}680681/// Contains static property keys and values.682pub mod static_property {683 use pallet_evm_coder_substrate::{684 execution::{Result, Error},685 };686 use alloc::format;687688 const EXPECT_CONVERT_ERROR: &str = "length < limit";689690 /// Keys.691 pub mod key {692 use super::*;693694 /// Key "baseURI".695 pub fn base_uri() -> up_data_structs::PropertyKey {696 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)697 }698699 /// Key "url".700 pub fn url() -> up_data_structs::PropertyKey {701 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)702 }703704 /// Key "suffix".705 pub fn suffix() -> up_data_structs::PropertyKey {706 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)707 }708709 /// Key "parentNft".710 pub fn parent_nft() -> up_data_structs::PropertyKey {711 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)712 }713 }714715 /// Convert `byte` to [`PropertyKey`].716 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {717 bytes.to_vec().try_into().map_err(|_| {718 Error::Revert(format!(719 "Property key is too long. Max length is {}.",720 up_data_structs::PropertyKey::bound()721 ))722 })723 }724725 /// Convert `bytes` to [`PropertyValue`].726 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {727 bytes.to_vec().try_into().map_err(|_| {728 Error::Revert(format!(729 "Property key is too long. Max length is {}.",730 up_data_structs::PropertyKey::bound()731 ))732 })733 }734}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! This module contains the implementation of pallet methods for evm.1819pub use pallet_evm::{PrecompileOutput, PrecompileResult, PrecompileHandle, account::CrossAccountId};20use pallet_evm_coder_substrate::{21 abi::AbiType,22 solidity_interface, ToLog,23 types::*,24 execution::{Result, Error, PreDispatch},25 frontier_contract,26};27use pallet_evm_coder_substrate::dispatch_to_evm;28use sp_std::{vec, vec::Vec};29use up_data_structs::{30 CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property, SponsoringRateLimit,31 SponsorshipState,32};3334use crate::{35 Pallet, CollectionHandle, Config, CollectionProperties, eth, SelfWeightOf, weights::WeightInfo,36};3738frontier_contract! {39 macro_rules! CollectionHandle_result {...}40 impl<T: Config> Contract for CollectionHandle<T> {...}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 /// The collection has been changed.63 CollectionChanged {64 /// Collection ID.65 #[indexed]66 collection_id: Address,67 },68}6970/// Does not always represent a full collection, for RFT it is either71/// collection (Implementing ERC721), or specific collection token (Implementing ERC20).72pub trait CommonEvmHandler {73 /// Raw compiled binary code of the contract stub74 const CODE: &'static [u8];7576 /// Call precompiled handle.77 fn call(self, handle: &mut impl PrecompileHandle) -> Option<PrecompileResult>;78}7980/// @title A contract that allows you to work with collections.81#[solidity_interface(name = Collection, enum(derive(PreDispatch)), enum_attr(weight))]82impl<T: Config> CollectionHandle<T>83where84 T::AccountId: From<[u8; 32]> + AsRef<[u8; 32]>,85{86 /// Set collection property.87 ///88 /// @param key Property key.89 /// @param value Propery value.90 #[solidity(hide)]91 #[weight(<SelfWeightOf<T>>::set_collection_properties(1))]92 fn set_collection_property(&mut self, caller: Caller, key: String, value: Bytes) -> Result<()> {93 let caller = T::CrossAccountId::from_eth(caller);94 let key = <Vec<u8>>::from(key)95 .try_into()96 .map_err(|_| "key too large")?;97 let value = value.0.try_into().map_err(|_| "value too large")?;9899 <Pallet<T>>::set_collection_property(self, &caller, Property { key, value })100 .map_err(dispatch_to_evm::<T>)101 }102103 /// Set collection properties.104 ///105 /// @param properties Vector of properties key/value pair.106 #[weight(<SelfWeightOf<T>>::set_collection_properties(properties.len() as u32))]107 fn set_collection_properties(108 &mut self,109 caller: Caller,110 properties: Vec<eth::Property>,111 ) -> Result<()> {112 let caller = T::CrossAccountId::from_eth(caller);113114 let properties = properties115 .into_iter()116 .map(eth::Property::try_into)117 .collect::<Result<Vec<_>>>()?;118119 <Pallet<T>>::set_collection_properties(self, &caller, properties.into_iter())120 .map_err(dispatch_to_evm::<T>)121 }122123 /// Delete collection property.124 ///125 /// @param key Property key.126 #[solidity(hide)]127 #[weight(<SelfWeightOf<T>>::delete_collection_properties(1))]128 fn delete_collection_property(&mut self, caller: Caller, key: String) -> Result<()> {129 let caller = T::CrossAccountId::from_eth(caller);130 let key = <Vec<u8>>::from(key)131 .try_into()132 .map_err(|_| "key too large")?;133134 <Pallet<T>>::delete_collection_property(self, &caller, key).map_err(dispatch_to_evm::<T>)135 }136137 /// Delete collection properties.138 ///139 /// @param keys Properties keys.140 #[weight(<SelfWeightOf<T>>::delete_collection_properties(keys.len() as u32))]141 fn delete_collection_properties(&mut self, caller: Caller, keys: Vec<String>) -> Result<()> {142 let caller = T::CrossAccountId::from_eth(caller);143 let keys = keys144 .into_iter()145 .map(|key| {146 <Vec<u8>>::from(key)147 .try_into()148 .map_err(|_| Error::Revert("key too large".into()))149 })150 .collect::<Result<Vec<_>>>()?;151152 <Pallet<T>>::delete_collection_properties(self, &caller, keys.into_iter())153 .map_err(dispatch_to_evm::<T>)154 }155156 /// Get collection property.157 ///158 /// @dev Throws error if key not found.159 ///160 /// @param key Property key.161 /// @return bytes The property corresponding to the key.162 fn collection_property(&self, key: String) -> Result<Bytes> {163 let key = <Vec<u8>>::from(key)164 .try_into()165 .map_err(|_| "key too large")?;166167 let props = CollectionProperties::<T>::get(self.id);168 let prop = props.get(&key).ok_or("key not found")?;169170 Ok(Bytes(prop.to_vec()))171 }172173 /// Get collection properties.174 ///175 /// @param keys Properties keys. Empty keys for all propertyes.176 /// @return Vector of properties key/value pairs.177 fn collection_properties(&self, keys: Vec<String>) -> Result<Vec<eth::Property>> {178 let keys = keys179 .into_iter()180 .map(|key| {181 <Vec<u8>>::from(key)182 .try_into()183 .map_err(|_| Error::Revert("key too large".into()))184 })185 .collect::<Result<Vec<_>>>()?;186187 let properties = Pallet::<T>::filter_collection_properties(188 self.id,189 if keys.is_empty() { None } else { Some(keys) },190 )191 .map_err(dispatch_to_evm::<T>)?;192193 let properties = properties194 .into_iter()195 .map(Property::try_into)196 .collect::<Result<Vec<_>>>()?;197 Ok(properties)198 }199200 /// Set the sponsor of the collection.201 ///202 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.203 ///204 /// @param sponsor Address of the sponsor from whose account funds will be debited for operations with the contract.205 #[solidity(hide)]206 fn set_collection_sponsor(&mut self, caller: Caller, sponsor: Address) -> Result<()> {207 self.consume_store_reads_and_writes(1, 1)?;208209 let caller = T::CrossAccountId::from_eth(caller);210211 let sponsor = T::CrossAccountId::from_eth(sponsor);212 self.set_sponsor(&caller, sponsor.as_sub().clone())213 .map_err(dispatch_to_evm::<T>)214 }215216 /// Set the sponsor of the collection.217 ///218 /// @dev In order for sponsorship to work, it must be confirmed on behalf of the sponsor.219 ///220 /// @param sponsor Cross account address of the sponsor from whose account funds will be debited for operations with the contract.221 fn set_collection_sponsor_cross(222 &mut self,223 caller: Caller,224 sponsor: eth::CrossAddress,225 ) -> Result<()> {226 self.consume_store_reads_and_writes(1, 1)?;227228 let caller = T::CrossAccountId::from_eth(caller);229230 let sponsor = sponsor.into_sub_cross_account::<T>()?;231 self.set_sponsor(&caller, sponsor.as_sub().clone())232 .map_err(dispatch_to_evm::<T>)233 }234235 /// Whether there is a pending sponsor.236 fn has_collection_pending_sponsor(&self) -> Result<bool> {237 Ok(matches!(238 self.collection.sponsorship,239 SponsorshipState::Unconfirmed(_)240 ))241 }242243 /// Collection sponsorship confirmation.244 ///245 /// @dev After setting the sponsor for the collection, it must be confirmed with this function.246 fn confirm_collection_sponsorship(&mut self, caller: Caller) -> Result<()> {247 self.consume_store_writes(1)?;248249 let caller = T::CrossAccountId::from_eth(caller);250 self.confirm_sponsorship(caller.as_sub())251 .map_err(dispatch_to_evm::<T>)252 }253254 /// Remove collection sponsor.255 fn remove_collection_sponsor(&mut self, caller: Caller) -> Result<()> {256 self.consume_store_reads_and_writes(1, 1)?;257 let caller = T::CrossAccountId::from_eth(caller);258 self.remove_sponsor(&caller).map_err(dispatch_to_evm::<T>)259 }260261 /// Get current sponsor.262 ///263 /// @return Tuble with sponsor address and his substrate mirror. If there is no confirmed sponsor error "Contract has no sponsor" throw.264 fn collection_sponsor(&self) -> Result<eth::CrossAddress> {265 let sponsor = match self.collection.sponsorship.sponsor() {266 Some(sponsor) => sponsor,267 None => return Ok(Default::default()),268 };269270 Ok(eth::CrossAddress::from_sub::<T>(sponsor))271 }272273 /// Get current collection limits.274 ///275 /// @return Array of collection limits276 fn collection_limits(&self) -> Result<Vec<eth::CollectionLimit>> {277 let limits = &self.collection.limits;278279 Ok(vec![280 eth::CollectionLimit::new(281 eth::CollectionLimitField::AccountTokenOwnership,282 limits.account_token_ownership_limit,283 ),284 eth::CollectionLimit::new(285 eth::CollectionLimitField::SponsoredDataSize,286 limits.sponsored_data_size,287 ),288 limits289 .sponsored_data_rate_limit290 .and_then(|limit| {291 if let SponsoringRateLimit::Blocks(blocks) = limit {292 Some(eth::CollectionLimit::new(293 eth::CollectionLimitField::SponsoredDataRateLimit,294 Some(blocks),295 ))296 } else {297 None298 }299 })300 .unwrap_or_else(|| {301 eth::CollectionLimit::new(302 eth::CollectionLimitField::SponsoredDataRateLimit,303 Default::default(),304 )305 }),306 eth::CollectionLimit::new(eth::CollectionLimitField::TokenLimit, limits.token_limit),307 eth::CollectionLimit::new(308 eth::CollectionLimitField::SponsorTransferTimeout,309 limits.sponsor_transfer_timeout,310 ),311 eth::CollectionLimit::new(312 eth::CollectionLimitField::SponsorApproveTimeout,313 limits.sponsor_approve_timeout,314 ),315 eth::CollectionLimit::new(316 eth::CollectionLimitField::OwnerCanTransfer,317 limits.owner_can_transfer.map(u32::from),318 ),319 eth::CollectionLimit::new(320 eth::CollectionLimitField::OwnerCanDestroy,321 limits.owner_can_destroy.map(u32::from),322 ),323 eth::CollectionLimit::new(324 eth::CollectionLimitField::TransferEnabled,325 limits.transfers_enabled.map(u32::from),326 ),327 ])328 }329330 /// Set limits for the collection.331 /// @dev Throws error if limit not found.332 /// @param limit Some limit.333 #[solidity(rename_selector = "setCollectionLimit")]334 fn set_collection_limit(&mut self, caller: Caller, limit: eth::CollectionLimit) -> Result<()> {335 self.consume_store_reads_and_writes(1, 1)?;336337 if !limit.has_value() {338 return Err(Error::Revert("user can't disable limits".into()));339 }340341 let caller = T::CrossAccountId::from_eth(caller);342 <Pallet<T>>::update_limits(&caller, self, limit.try_into()?).map_err(dispatch_to_evm::<T>)343 }344345 /// Get contract address.346 fn contract_address(&self) -> Result<Address> {347 Ok(crate::eth::collection_id_to_address(self.id))348 }349350 /// Add collection admin.351 /// @param newAdmin Cross account administrator address.352 fn add_collection_admin_cross(353 &mut self,354 caller: Caller,355 new_admin: eth::CrossAddress,356 ) -> Result<()> {357 self.consume_store_reads_and_writes(2, 2)?;358359 let caller = T::CrossAccountId::from_eth(caller);360 let new_admin = new_admin.into_sub_cross_account::<T>()?;361 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;362 Ok(())363 }364365 /// Remove collection admin.366 /// @param admin Cross account administrator address.367 fn remove_collection_admin_cross(368 &mut self,369 caller: Caller,370 admin: eth::CrossAddress,371 ) -> Result<()> {372 self.consume_store_reads_and_writes(2, 2)?;373374 let caller = T::CrossAccountId::from_eth(caller);375 let admin = admin.into_sub_cross_account::<T>()?;376 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;377 Ok(())378 }379380 /// Add collection admin.381 /// @param newAdmin Address of the added administrator.382 #[solidity(hide)]383 fn add_collection_admin(&mut self, caller: Caller, new_admin: Address) -> Result<()> {384 self.consume_store_reads_and_writes(2, 2)?;385386 let caller = T::CrossAccountId::from_eth(caller);387 let new_admin = T::CrossAccountId::from_eth(new_admin);388 <Pallet<T>>::toggle_admin(self, &caller, &new_admin, true).map_err(dispatch_to_evm::<T>)?;389 Ok(())390 }391392 /// Remove collection admin.393 ///394 /// @param admin Address of the removed administrator.395 #[solidity(hide)]396 fn remove_collection_admin(&mut self, caller: Caller, admin: Address) -> Result<()> {397 self.consume_store_reads_and_writes(2, 2)?;398399 let caller = T::CrossAccountId::from_eth(caller);400 let admin = T::CrossAccountId::from_eth(admin);401 <Pallet<T>>::toggle_admin(self, &caller, &admin, false).map_err(dispatch_to_evm::<T>)?;402 Ok(())403 }404405 /// Toggle accessibility of collection nesting.406 ///407 /// @param enable If "true" degenerates to nesting: 'Owner' else to nesting: 'Disabled'408 #[solidity(rename_selector = "setCollectionNesting")]409 fn set_nesting_bool(&mut self, caller: Caller, enable: bool) -> Result<()> {410 self.consume_store_reads_and_writes(1, 1)?;411412 let caller = T::CrossAccountId::from_eth(caller);413414 let mut permissions = self.collection.permissions.clone();415 let mut nesting = permissions.nesting().clone();416 nesting.token_owner = enable;417 nesting.restricted = None;418 permissions.nesting = Some(nesting);419420 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)421 }422423 /// Toggle accessibility of collection nesting.424 ///425 /// @param enable If "true" degenerates to nesting: {OwnerRestricted: [1, 2, 3]} else to nesting: 'Disabled'426 /// @param collections Addresses of collections that will be available for nesting.427 #[solidity(rename_selector = "setCollectionNesting")]428 fn set_nesting(429 &mut self,430 caller: Caller,431 enable: bool,432 collections: Vec<Address>,433 ) -> Result<()> {434 self.consume_store_reads_and_writes(1, 1)?;435436 if collections.is_empty() {437 return Err("no addresses provided".into());438 }439 let caller = T::CrossAccountId::from_eth(caller);440441 let mut permissions = self.collection.permissions.clone();442 match enable {443 false => {444 let mut nesting = permissions.nesting().clone();445 nesting.token_owner = false;446 nesting.restricted = None;447 permissions.nesting = Some(nesting);448 }449 true => {450 let mut bv = OwnerRestrictedSet::new();451 for i in collections {452 bv.try_insert(crate::eth::map_eth_to_id(&i).ok_or_else(|| {453 Error::Revert("Can't convert address into collection id".into())454 })?)455 .map_err(|_| "too many collections")?;456 }457 let mut nesting = permissions.nesting().clone();458 nesting.token_owner = true;459 nesting.restricted = Some(bv);460 permissions.nesting = Some(nesting);461 }462 };463464 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)465 }466467 /// Returns nesting for a collection468 #[solidity(rename_selector = "collectionNestingRestrictedCollectionIds")]469 fn collection_nesting_restricted_ids(&self) -> Result<eth::CollectionNesting> {470 let nesting = self.collection.permissions.nesting();471472 Ok(eth::CollectionNesting::new(473 nesting.token_owner,474 nesting475 .restricted476 .clone()477 .map(|b| b.0.into_inner().iter().map(|id| id.0.into()).collect())478 .unwrap_or_default(),479 ))480 }481482 /// Returns permissions for a collection483 fn collection_nesting_permissions(&self) -> Result<Vec<eth::CollectionNestingPermission>> {484 let nesting = self.collection.permissions.nesting();485 Ok(vec![486 eth::CollectionNestingPermission::new(487 eth::CollectionPermissionField::CollectionAdmin,488 nesting.collection_admin,489 ),490 eth::CollectionNestingPermission::new(491 eth::CollectionPermissionField::TokenOwner,492 nesting.token_owner,493 ),494 ])495 }496 /// Set the collection access method.497 /// @param mode Access mode498 fn set_collection_access(&mut self, caller: Caller, mode: eth::AccessMode) -> Result<()> {499 self.consume_store_reads_and_writes(1, 1)?;500501 let caller = T::CrossAccountId::from_eth(caller);502 let permissions = CollectionPermissions {503 access: Some(mode.into()),504 ..Default::default()505 };506 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)507 }508509 /// Checks that user allowed to operate with collection.510 ///511 /// @param user User address to check.512 fn allowlisted_cross(&self, user: eth::CrossAddress) -> Result<bool> {513 let user = user.into_sub_cross_account::<T>()?;514 Ok(Pallet::<T>::allowed(self.id, user))515 }516517 /// Add the user to the allowed list.518 ///519 /// @param user Address of a trusted user.520 #[solidity(hide)]521 fn add_to_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {522 self.consume_store_writes(1)?;523524 let caller = T::CrossAccountId::from_eth(caller);525 let user = T::CrossAccountId::from_eth(user);526 <Pallet<T>>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;527 Ok(())528 }529530 /// Add user to allowed list.531 ///532 /// @param user User cross account address.533 fn add_to_collection_allow_list_cross(534 &mut self,535 caller: Caller,536 user: eth::CrossAddress,537 ) -> Result<()> {538 self.consume_store_writes(1)?;539540 let caller = T::CrossAccountId::from_eth(caller);541 let user = user.into_sub_cross_account::<T>()?;542 Pallet::<T>::toggle_allowlist(self, &caller, &user, true).map_err(dispatch_to_evm::<T>)?;543 Ok(())544 }545546 /// Remove the user from the allowed list.547 ///548 /// @param user Address of a removed user.549 #[solidity(hide)]550 fn remove_from_collection_allow_list(&mut self, caller: Caller, user: Address) -> Result<()> {551 self.consume_store_writes(1)?;552553 let caller = T::CrossAccountId::from_eth(caller);554 let user = T::CrossAccountId::from_eth(user);555 <Pallet<T>>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;556 Ok(())557 }558559 /// Remove user from allowed list.560 ///561 /// @param user User cross account address.562 fn remove_from_collection_allow_list_cross(563 &mut self,564 caller: Caller,565 user: eth::CrossAddress,566 ) -> Result<()> {567 self.consume_store_writes(1)?;568569 let caller = T::CrossAccountId::from_eth(caller);570 let user = user.into_sub_cross_account::<T>()?;571 Pallet::<T>::toggle_allowlist(self, &caller, &user, false).map_err(dispatch_to_evm::<T>)?;572 Ok(())573 }574575 /// Switch permission for minting.576 ///577 /// @param mode Enable if "true".578 fn set_collection_mint_mode(&mut self, caller: Caller, mode: bool) -> Result<()> {579 self.consume_store_reads_and_writes(1, 1)?;580581 let caller = T::CrossAccountId::from_eth(caller);582 let permissions = CollectionPermissions {583 mint_mode: Some(mode),584 ..Default::default()585 };586 <Pallet<T>>::update_permissions(&caller, self, permissions).map_err(dispatch_to_evm::<T>)587 }588589 /// Check that account is the owner or admin of the collection590 ///591 /// @param user account to verify592 /// @return "true" if account is the owner or admin593 #[solidity(hide, rename_selector = "isOwnerOrAdmin")]594 fn is_owner_or_admin_eth(&self, user: Address) -> Result<bool> {595 let user = T::CrossAccountId::from_eth(user);596 Ok(self.is_owner_or_admin(&user))597 }598599 /// Check that account is the owner or admin of the collection600 ///601 /// @param user User cross account to verify602 /// @return "true" if account is the owner or admin603 fn is_owner_or_admin_cross(&self, user: eth::CrossAddress) -> Result<bool> {604 let user = user.into_sub_cross_account::<T>()?;605 Ok(self.is_owner_or_admin(&user))606 }607608 /// Returns collection type609 ///610 /// @return `Fungible` or `NFT` or `ReFungible`611 fn unique_collection_type(&self) -> Result<String> {612 let mode = match self.collection.mode {613 CollectionMode::Fungible(_) => "Fungible",614 CollectionMode::NFT => "NFT",615 CollectionMode::ReFungible => "ReFungible",616 };617 Ok(mode.into())618 }619620 /// Get collection owner.621 ///622 /// @return Tuble with sponsor address and his substrate mirror.623 /// If address is canonical then substrate mirror is zero and vice versa.624 fn collection_owner(&self) -> Result<eth::CrossAddress> {625 Ok(eth::CrossAddress::from_sub_cross_account::<T>(626 &T::CrossAccountId::from_sub(self.owner.clone()),627 ))628 }629630 /// Changes collection owner to another account631 ///632 /// @dev Owner can be changed only by current owner633 /// @param newOwner new owner account634 #[solidity(hide, rename_selector = "changeCollectionOwner")]635 fn set_owner(&mut self, caller: Caller, new_owner: Address) -> Result<()> {636 self.consume_store_writes(1)?;637638 let caller = T::CrossAccountId::from_eth(caller);639 let new_owner = T::CrossAccountId::from_eth(new_owner);640 self.change_owner(caller, new_owner)641 .map_err(dispatch_to_evm::<T>)642 }643644 /// Get collection administrators645 ///646 /// @return Vector of tuples with admins address and his substrate mirror.647 /// If address is canonical then substrate mirror is zero and vice versa.648 fn collection_admins(&self) -> Result<Vec<eth::CrossAddress>> {649 let result = crate::IsAdmin::<T>::iter_prefix((self.id,))650 .map(|(admin, _)| eth::CrossAddress::from_sub_cross_account::<T>(&admin))651 .collect();652 Ok(result)653 }654655 /// Changes collection owner to another account656 ///657 /// @dev Owner can be changed only by current owner658 /// @param newOwner new owner cross account659 fn change_collection_owner_cross(660 &mut self,661 caller: Caller,662 new_owner: eth::CrossAddress,663 ) -> Result<()> {664 self.consume_store_writes(1)?;665666 let caller = T::CrossAccountId::from_eth(caller);667 let new_owner = new_owner.into_sub_cross_account::<T>()?;668 self.change_owner(caller, new_owner)669 .map_err(dispatch_to_evm::<T>)670 }671}672673/// Contains static property keys and values.674pub mod static_property {675 use pallet_evm_coder_substrate::{676 execution::{Result, Error},677 };678 use alloc::format;679680 const EXPECT_CONVERT_ERROR: &str = "length < limit";681682 /// Keys.683 pub mod key {684 use super::*;685686 /// Key "baseURI".687 pub fn base_uri() -> up_data_structs::PropertyKey {688 property_key_from_bytes(b"baseURI").expect(EXPECT_CONVERT_ERROR)689 }690691 /// Key "url".692 pub fn url() -> up_data_structs::PropertyKey {693 property_key_from_bytes(b"URI").expect(EXPECT_CONVERT_ERROR)694 }695696 /// Key "suffix".697 pub fn suffix() -> up_data_structs::PropertyKey {698 property_key_from_bytes(b"URISuffix").expect(EXPECT_CONVERT_ERROR)699 }700701 /// Key "parentNft".702 pub fn parent_nft() -> up_data_structs::PropertyKey {703 property_key_from_bytes(b"parentNft").expect(EXPECT_CONVERT_ERROR)704 }705 }706707 /// Convert `byte` to [`PropertyKey`].708 pub fn property_key_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyKey> {709 bytes.to_vec().try_into().map_err(|_| {710 Error::Revert(format!(711 "Property key is too long. Max length is {}.",712 up_data_structs::PropertyKey::bound()713 ))714 })715 }716717 /// Convert `bytes` to [`PropertyValue`].718 pub fn property_value_from_bytes(bytes: &[u8]) -> Result<up_data_structs::PropertyValue> {719 bytes.to_vec().try_into().map_err(|_| {720 Error::Revert(format!(721 "Property key is too long. Max length is {}.",722 up_data_structs::PropertyKey::bound()723 ))724 })725 }726}pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -68,7 +68,6 @@
dispatch::Pays,
transactional, fail,
};
-use pallet_evm::GasWeightMapping;
use up_data_structs::{
AccessMode, COLLECTION_NUMBER_LIMIT, Collection, RpcCollection, CollectionFlags,
RpcCollectionFlags, CollectionId, CreateItemData, MAX_TOKEN_PREFIX_LENGTH,
@@ -101,7 +100,7 @@
/// Collection handle contains information about collection data and id.
/// Also provides functionality to count consumed gas.
///
-/// CollectionHandle is used as a generic wrapper for collections of all types.
+/// CollectionHandle is used as a generic wrapper for collections of all types (except native fungible).
/// It allows to perform common operations and queries on any collection type,
/// both completely general for all, as well as their respective implementations of [`CommonCollectionOperations`].
#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
@@ -153,7 +152,7 @@
&self,
reads: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_reads(self.recorder(), reads)
+ self.recorder().consume_store_reads(reads)
}
/// Consume gas for writing.
@@ -161,7 +160,7 @@
&self,
writes: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_writes(self.recorder(), writes)
+ self.recorder().consume_store_writes(writes)
}
/// Consume gas for reading and writing.
@@ -170,7 +169,8 @@
reads: u64,
writes: u64,
) -> pallet_evm_coder_substrate::execution::Result<()> {
- consume_store_reads_and_writes(self.recorder(), reads, writes)
+ self.recorder()
+ .consume_store_reads_and_writes(reads, writes)
}
/// Save collection to storage.
@@ -441,7 +441,7 @@
const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);
/// Collection id for native fungible collction.
- pub const NATIVE_FINGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);
+ pub const NATIVE_FUNGIBLE_COLLECTION_ID: CollectionId = CollectionId(0);
#[pallet::pallet]
#[pallet::storage_version(STORAGE_VERSION)]
@@ -2322,48 +2322,4 @@
PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,
}
}
-}
-
-/// Consume gas for reading.
-pub fn consume_store_reads<T: Config>(
- recorder: &SubstrateRecorder<T>,
- reads: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- <T as frame_system::Config>::DbWeight::get()
- .read
- .saturating_mul(reads),
- // TODO: measure proof
- 0,
- )))
-}
-
-/// Consume gas for writing.
-pub fn consume_store_writes<T: Config>(
- recorder: &SubstrateRecorder<T>,
- writes: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- <T as frame_system::Config>::DbWeight::get()
- .write
- .saturating_mul(writes),
- // TODO: measure proof
- 0,
- )))
-}
-
-/// Consume gas for reading and writing.
-pub fn consume_store_reads_and_writes<T: Config>(
- recorder: &SubstrateRecorder<T>,
- reads: u64,
- writes: u64,
-) -> pallet_evm_coder_substrate::execution::Result<()> {
- let weight = <T as frame_system::Config>::DbWeight::get();
- let reads = weight.read.saturating_mul(reads);
- let writes = weight.read.saturating_mul(writes);
- recorder.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
- reads.saturating_add(writes),
- // TODO: measure proof
- 0,
- )))
}
pallets/evm-coder-substrate/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-coder-substrate/src/lib.rs
+++ b/pallets/evm-coder-substrate/src/lib.rs
@@ -37,7 +37,7 @@
ExitError, ExitRevert, ExitSucceed, GasWeightMapping, PrecompileFailure, PrecompileOutput,
PrecompileResult, PrecompileHandle,
};
-use sp_core::H160;
+use sp_core::{Get, H160};
// #[cfg(feature = "runtime-benchmarks")]
// pub mod benchmarking;
pub mod execution;
@@ -204,6 +204,40 @@
Err(Error::Error(e)) => Err(e.into()),
})
}
+
+ /// Consume gas for reading.
+ pub fn consume_store_reads(&self, reads: u64) -> execution::Result<()> {
+ self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
+ <T as frame_system::Config>::DbWeight::get()
+ .read
+ .saturating_mul(reads),
+ // TODO: measure proof
+ 0,
+ )))
+ }
+
+ /// Consume gas for writing.
+ pub fn consume_store_writes(&self, writes: u64) -> execution::Result<()> {
+ self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
+ <T as frame_system::Config>::DbWeight::get()
+ .write
+ .saturating_mul(writes),
+ // TODO: measure proof
+ 0,
+ )))
+ }
+
+ /// Consume gas for reading and writing.
+ pub fn consume_store_reads_and_writes(&self, reads: u64, writes: u64) -> execution::Result<()> {
+ let weight = <T as frame_system::Config>::DbWeight::get();
+ let reads = weight.read.saturating_mul(reads);
+ let writes = weight.read.saturating_mul(writes);
+ self.consume_gas(T::GasWeightMapping::weight_to_gas(Weight::from_parts(
+ reads.saturating_add(writes),
+ // TODO: measure proof
+ 0,
+ )))
+ }
}
pub fn dispatch_to_evm<T: Config>(err: DispatchError) -> execution::Error {
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -440,7 +440,7 @@
collection_id: CollectionId,
address: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
@@ -471,7 +471,7 @@
collection_id: CollectionId,
address: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
@@ -501,7 +501,7 @@
collection_id: CollectionId,
new_owner: T::AccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -533,7 +533,7 @@
collection_id: CollectionId,
new_admin_id: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -562,7 +562,7 @@
collection_id: CollectionId,
account_id: T::CrossAccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -590,7 +590,7 @@
collection_id: CollectionId,
new_sponsor: T::AccountId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -617,7 +617,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = ensure_signed(origin)?;
@@ -640,7 +640,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -920,7 +920,7 @@
collection_id: CollectionId,
value: bool,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1175,7 +1175,7 @@
collection_id: CollectionId,
new_limit: CollectionLimits,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1202,7 +1202,7 @@
collection_id: CollectionId,
new_permission: CollectionPermissions,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
@@ -1273,7 +1273,7 @@
origin: OriginFor<T>,
collection_id: CollectionId,
) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
ensure_root(origin)?;
runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -100,12 +100,11 @@
}
fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {
- if collection_id == pallet_common::NATIVE_FINGIBLE_COLLECTION_ID {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
fail!(<pallet_common::Error<T>>::UnsupportedOperation);
}
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
- collection.check_is_internal()?;
match collection.mode {
CollectionMode::ReFungible => {
@@ -122,7 +121,7 @@
}
fn dispatch(collection_id: CollectionId) -> Result<Self, DispatchError> {
- if collection_id == CollectionId(0) {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
return Ok(Self::NativeFungible(NativeFungibleHandle::new()));
}
@@ -188,7 +187,7 @@
}
fn call(handle: &mut impl PrecompileHandle) -> Option<PrecompileResult> {
if let Some(collection_id) = map_eth_to_id(&handle.code_address()) {
- if collection_id == CollectionId(0) {
+ if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {
<NativeFungibleHandle<T>>::new().call(handle)
} else {
let collection = <CollectionHandle<T>>::new_with_gas_limit(