difftreelog
feat benchmark property calls
in: master
30 files changed
Cargo.lockdiffbeforeafterboth--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5913,6 +5913,7 @@
dependencies = [
"evm-coder",
"fp-evm-mapping",
+ "frame-benchmarking",
"frame-support",
"frame-system",
"pallet-evm",
@@ -6649,6 +6650,7 @@
"frame-support",
"frame-system",
"pallet-common",
+ "pallet-evm",
"parity-scale-codec 3.1.2",
"scale-info",
"sp-std",
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -41,6 +41,10 @@
bench-evm-migration:
make _bench PALLET=evm-migration
+.PHONY: bench-common
+bench-common:
+ make _bench PALLET=common
+
.PHONY: bench-unique
bench-unique:
make _bench PALLET=unique
pallets/common/Cargo.tomldiffbeforeafterboth--- a/pallets/common/Cargo.toml
+++ b/pallets/common/Cargo.toml
@@ -25,6 +25,7 @@
scale-info = { version = "2.0.1", default-features = false, features = [
"derive",
] }
+frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.21" }
[features]
default = ["std"]
@@ -37,4 +38,6 @@
"up-data-structs/std",
"pallet-evm/std",
]
-runtime-benchmarks = []
+runtime-benchmarks = [
+ "frame-benchmarking"
+]
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -15,11 +15,13 @@
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
use sp_std::vec::Vec;
-use crate::{Config, CollectionHandle};
+use crate::{Config, CollectionHandle, Pallet};
+use pallet_evm::account::CrossAccountId;
+use frame_benchmarking::{benchmarks, account};
use up_data_structs::{
- CollectionMode, CreateCollectionData, CollectionId, MAX_COLLECTION_NAME_LENGTH,
- MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, OFFCHAIN_SCHEMA_LIMIT,
- CONST_ON_CHAIN_SCHEMA_LIMIT,
+ CollectionMode, CreateCollectionData, CollectionId, Property, PropertyKey, PropertyValue,
+ MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+ OFFCHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, MAX_PROPERTIES_PER_ITEM,
};
use frame_support::{
traits::{Currency, Get},
@@ -29,6 +31,8 @@
use core::convert::TryInto;
use sp_runtime::DispatchError;
+const SEED: u32 = 1;
+
pub fn create_data<const S: u32>() -> BoundedVec<u8, ConstU32<S>> {
create_var_data::<S>(S)
}
@@ -52,6 +56,22 @@
.try_into()
.unwrap()
}
+pub fn property_key(id: usize) -> PropertyKey {
+ #[cfg(not(feature = "std"))]
+ use alloc::string::ToString;
+ let mut data = create_data();
+ // No DerefMut available for .fill
+ for i in 0..data.len() {
+ data[i] = b'0';
+ }
+ let bytes = id.to_string();
+ let len = data.len();
+ data[len - bytes.len()..].copy_from_slice(&bytes.as_bytes());
+ data
+}
+pub fn property_value() -> PropertyValue {
+ create_data()
+}
pub fn create_collection_raw<T: Config, R>(
owner: T::AccountId,
@@ -83,6 +103,14 @@
.and_then(CollectionHandle::try_get)
.map(cast)
}
+fn create_collection<T: Config>(owner: T::AccountId) -> Result<CollectionHandle<T>, DispatchError> {
+ create_collection_raw(
+ owner,
+ CollectionMode::NFT,
+ |owner, data| <Pallet<T>>::init_collection(owner, data),
+ |h| h,
+ )
+}
/// Helper macros, which handles all benchmarking preparation in semi-declarative way
///
@@ -125,3 +153,31 @@
};
() => {}
}
+
+benchmarks! {
+ set_collection_properties {
+ let b in 0..MAX_PROPERTIES_PER_ITEM;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let props = (0..b).map(|p| Property {
+ key: property_key(p as usize),
+ value: property_value(),
+ }).collect::<Vec<_>>();
+ }: {<Pallet<T>>::set_collection_properties(&collection, &owner, props)?}
+
+ delete_collection_properties {
+ let b in 0..MAX_PROPERTIES_PER_ITEM;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let props = (0..b).map(|p| Property {
+ key: property_key(p as usize),
+ value: property_value(),
+ }).collect::<Vec<_>>();
+ <Pallet<T>>::set_collection_properties(&collection, &owner, props)?;
+ let to_delete = (0..b).map(|p| property_key(p as usize)).collect::<Vec<_>>();
+ }: {<Pallet<T>>::delete_collection_properties(&collection, &owner, to_delete)?}
+}
pallets/common/src/lib.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#![cfg_attr(not(feature = "std"), no_std)]1819use core::ops::{Deref, DerefMut};20use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};21use sp_std::vec::Vec;22use pallet_evm::account::CrossAccountId;23use frame_support::{24 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},25 ensure,26 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},27 BoundedVec,28 weights::Pays,29 transactional,30};31use pallet_evm::GasWeightMapping;32use up_data_structs::{33 COLLECTION_NUMBER_LIMIT,34 Collection,35 RpcCollection,36 CollectionId,37 CreateItemData,38 MAX_TOKEN_PREFIX_LENGTH,39 COLLECTION_ADMINS_LIMIT,40 TokenId,41 CollectionStats,42 MAX_TOKEN_OWNERSHIP,43 CollectionMode,44 NFT_SPONSOR_TRANSFER_TIMEOUT,45 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,46 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,47 MAX_SPONSOR_TIMEOUT,48 CUSTOM_DATA_LIMIT,49 CollectionLimits,50 CreateCollectionData,51 SponsorshipState,52 CreateItemExData,53 SponsoringRateLimit,54 budget::Budget,55 COLLECTION_FIELD_LIMIT,56 CollectionField,57 PhantomType,58 Property,59 Properties,60 PropertiesPermissionMap,61 PropertyKey,62 PropertyValue,63 PropertyPermission,64 PropertiesError,65 PropertyKeyPermission,66 TokenData,67 TrySetProperty,68 PropertyScope,69 // RMRK70 RmrkCollectionInfo,71 RmrkInstanceInfo,72 RmrkResourceInfo,73 RmrkPropertyInfo,74 RmrkBaseInfo,75 RmrkPartType,76 RmrkTheme,77 RmrkNftChild,78};7980pub use pallet::*;81use sp_core::H160;82use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};83#[cfg(feature = "runtime-benchmarks")]84pub mod benchmarking;85pub mod dispatch;86pub mod erc;87pub mod eth;8889#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]90pub struct CollectionHandle<T: Config> {91 pub id: CollectionId,92 collection: Collection<T::AccountId>,93 pub recorder: SubstrateRecorder<T>,94}95impl<T: Config> WithRecorder<T> for CollectionHandle<T> {96 fn recorder(&self) -> &SubstrateRecorder<T> {97 &self.recorder98 }99 fn into_recorder(self) -> SubstrateRecorder<T> {100 self.recorder101 }102}103impl<T: Config> CollectionHandle<T> {104 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {105 <CollectionById<T>>::get(id).map(|collection| Self {106 id,107 collection,108 recorder: SubstrateRecorder::new(gas_limit),109 })110 }111 pub fn new(id: CollectionId) -> Option<Self> {112 Self::new_with_gas_limit(id, u64::MAX)113 }114 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {115 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)116 }117 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {118 self.recorder119 .consume_gas(T::GasWeightMapping::weight_to_gas(120 <T as frame_system::Config>::DbWeight::get()121 .read122 .saturating_mul(reads),123 ))124 }125 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {126 self.recorder127 .consume_gas(T::GasWeightMapping::weight_to_gas(128 <T as frame_system::Config>::DbWeight::get()129 .write130 .saturating_mul(writes),131 ))132 }133 pub fn save(self) -> DispatchResult {134 <CollectionById<T>>::insert(self.id, self.collection);135 Ok(())136 }137}138impl<T: Config> Deref for CollectionHandle<T> {139 type Target = Collection<T::AccountId>;140141 fn deref(&self) -> &Self::Target {142 &self.collection143 }144}145146impl<T: Config> DerefMut for CollectionHandle<T> {147 fn deref_mut(&mut self) -> &mut Self::Target {148 &mut self.collection149 }150}151152impl<T: Config> CollectionHandle<T> {153 pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {154 ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);155 Ok(())156 }157 pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {158 *subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))159 }160 pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {161 ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);162 Ok(())163 }164 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {165 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)166 }167 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {168 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)169 }170 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {171 ensure!(172 <Allowlist<T>>::get((self.id, user)),173 <Error<T>>::AddressNotInAllowlist174 );175 Ok(())176 }177}178179#[frame_support::pallet]180pub mod pallet {181 use super::*;182 use pallet_evm::account;183 use dispatch::CollectionDispatch;184 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};185 use frame_system::pallet_prelude::*;186 use frame_support::traits::Currency;187 use up_data_structs::{TokenId, mapping::TokenAddressMapping};188 use scale_info::TypeInfo;189190 #[pallet::config]191 pub trait Config:192 frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config193 {194 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;195196 type Currency: Currency<Self::AccountId>;197198 #[pallet::constant]199 type CollectionCreationPrice: Get<200 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,201 >;202 type CollectionDispatch: CollectionDispatch<Self>;203204 type TreasuryAccountId: Get<Self::AccountId>;205206 type EvmTokenAddressMapping: TokenAddressMapping<H160>;207 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;208 }209210 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);211212 #[pallet::pallet]213 #[pallet::storage_version(STORAGE_VERSION)]214 #[pallet::generate_store(pub(super) trait Store)]215 pub struct Pallet<T>(_);216217 #[pallet::extra_constants]218 impl<T: Config> Pallet<T> {219 pub fn collection_admins_limit() -> u32 {220 COLLECTION_ADMINS_LIMIT221 }222 }223224 #[pallet::event]225 #[pallet::generate_deposit(pub fn deposit_event)]226 pub enum Event<T: Config> {227 /// New collection was created228 ///229 /// # Arguments230 ///231 /// * collection_id: Globally unique identifier of newly created collection.232 ///233 /// * mode: [CollectionMode] converted into u8.234 ///235 /// * account_id: Collection owner.236 CollectionCreated(CollectionId, u8, T::AccountId),237238 /// New collection was destroyed239 ///240 /// # Arguments241 ///242 /// * collection_id: Globally unique identifier of collection.243 CollectionDestroyed(CollectionId),244245 /// New item was created.246 ///247 /// # Arguments248 ///249 /// * collection_id: Id of the collection where item was created.250 ///251 /// * item_id: Id of an item. Unique within the collection.252 ///253 /// * recipient: Owner of newly created item254 ///255 /// * amount: Always 1 for NFT256 ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),257258 /// Collection item was burned.259 ///260 /// # Arguments261 ///262 /// * collection_id.263 ///264 /// * item_id: Identifier of burned NFT.265 ///266 /// * owner: which user has destroyed its tokens267 ///268 /// * amount: Always 1 for NFT269 ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),270271 /// Item was transferred272 ///273 /// * collection_id: Id of collection to which item is belong274 ///275 /// * item_id: Id of an item276 ///277 /// * sender: Original owner of item278 ///279 /// * recipient: New owner of item280 ///281 /// * amount: Always 1 for NFT282 Transfer(283 CollectionId,284 TokenId,285 T::CrossAccountId,286 T::CrossAccountId,287 u128,288 ),289290 /// * collection_id291 ///292 /// * item_id293 ///294 /// * sender295 ///296 /// * spender297 ///298 /// * amount299 Approved(300 CollectionId,301 TokenId,302 T::CrossAccountId,303 T::CrossAccountId,304 u128,305 ),306307 CollectionPropertySet(CollectionId, PropertyKey),308309 CollectionPropertyDeleted(CollectionId, PropertyKey),310311 TokenPropertySet(CollectionId, TokenId, PropertyKey),312313 TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),314315 PropertyPermissionSet(CollectionId, PropertyKey),316 }317318 #[pallet::error]319 pub enum Error<T> {320 /// This collection does not exist.321 CollectionNotFound,322 /// Sender parameter and item owner must be equal.323 MustBeTokenOwner,324 /// No permission to perform action325 NoPermission,326 /// Collection is not in mint mode.327 PublicMintingNotAllowed,328 /// Address is not in allow list.329 AddressNotInAllowlist,330331 /// Collection name can not be longer than 63 char.332 CollectionNameLimitExceeded,333 /// Collection description can not be longer than 255 char.334 CollectionDescriptionLimitExceeded,335 /// Token prefix can not be longer than 15 char.336 CollectionTokenPrefixLimitExceeded,337 /// Total collections bound exceeded.338 TotalCollectionsLimitExceeded,339 /// Exceeded max admin count340 CollectionAdminCountExceeded,341 /// Collection limit bounds per collection exceeded342 CollectionLimitBoundsExceeded,343 /// Tried to enable permissions which are only permitted to be disabled344 OwnerPermissionsCantBeReverted,345 /// Collection settings not allowing items transferring346 TransferNotAllowed,347 /// Account token limit exceeded per collection348 AccountTokenLimitExceeded,349 /// Collection token limit exceeded350 CollectionTokenLimitExceeded,351 /// Metadata flag frozen352 MetadataFlagFrozen,353354 /// Item not exists.355 TokenNotFound,356 /// Item balance not enough.357 TokenValueTooLow,358 /// Requested value more than approved.359 ApprovedValueTooLow,360 /// Tried to approve more than owned361 CantApproveMoreThanOwned,362363 /// Can't transfer tokens to ethereum zero address364 AddressIsZero,365 /// Target collection doesn't supports this operation366 UnsupportedOperation,367368 /// Not sufficient founds to perform action369 NotSufficientFounds,370371 /// Collection has nesting disabled372 NestingIsDisabled,373 /// Only owner may nest tokens under this collection374 OnlyOwnerAllowedToNest,375 /// Only tokens from specific collections may nest tokens under this376 SourceCollectionIsNotAllowedToNest,377378 /// Tried to store more data than allowed in collection field379 CollectionFieldSizeExceeded,380381 /// Tried to store more property data than allowed382 NoSpaceForProperty,383384 /// Tried to store more property keys than allowed385 PropertyLimitReached,386387 /// Property key is too long388 PropertyKeyIsTooLong,389390 /// Only ASCII letters, digits, and '_', '-' are allowed391 InvalidCharacterInPropertyKey,392393 /// Empty property keys are forbidden394 EmptyPropertyKey,395 }396397 #[pallet::storage]398 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;399 #[pallet::storage]400 pub type DestroyedCollectionCount<T> =401 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;402403 /// Collection info404 #[pallet::storage]405 pub type CollectionById<T> = StorageMap<406 Hasher = Blake2_128Concat,407 Key = CollectionId,408 Value = Collection<<T as frame_system::Config>::AccountId>,409 QueryKind = OptionQuery,410 >;411412 /// Collection properties413 #[pallet::storage]414 #[pallet::getter(fn collection_properties)]415 pub type CollectionProperties<T> = StorageMap<416 Hasher = Blake2_128Concat,417 Key = CollectionId,418 Value = Properties,419 QueryKind = ValueQuery,420 OnEmpty = up_data_structs::CollectionProperties,421 >;422423 #[pallet::storage]424 #[pallet::getter(fn property_permissions)]425 pub type CollectionPropertyPermissions<T> = StorageMap<426 Hasher = Blake2_128Concat,427 Key = CollectionId,428 Value = PropertiesPermissionMap,429 QueryKind = ValueQuery,430 >;431432 /// Large variable-size collection fields are extracted here433 #[pallet::storage]434 pub type CollectionData<T> = StorageNMap<435 Key = (436 Key<Twox64Concat, CollectionId>,437 Key<Twox64Concat, CollectionField>,438 ),439 Value = BoundedVec<u8, ConstU32<COLLECTION_FIELD_LIMIT>>,440 QueryKind = ValueQuery,441 >;442443 #[pallet::storage]444 pub type AdminAmount<T> = StorageMap<445 Hasher = Blake2_128Concat,446 Key = CollectionId,447 Value = u32,448 QueryKind = ValueQuery,449 >;450451 /// List of collection admins452 #[pallet::storage]453 pub type IsAdmin<T: Config> = StorageNMap<454 Key = (455 Key<Blake2_128Concat, CollectionId>,456 Key<Blake2_128Concat, T::CrossAccountId>,457 ),458 Value = bool,459 QueryKind = ValueQuery,460 >;461462 /// Allowlisted collection users463 #[pallet::storage]464 pub type Allowlist<T: Config> = StorageNMap<465 Key = (466 Key<Blake2_128Concat, CollectionId>,467 Key<Blake2_128Concat, T::CrossAccountId>,468 ),469 Value = bool,470 QueryKind = ValueQuery,471 >;472473 /// Not used by code, exists only to provide some types to metadata474 #[pallet::storage]475 pub type DummyStorageValue<T: Config> = StorageValue<476 Value = (477 CollectionStats,478 CollectionId,479 TokenId,480 PhantomType<TokenData<T::CrossAccountId>>,481 PhantomType<RpcCollection<T::AccountId>>,482 // RMRK483 PhantomType<RmrkCollectionInfo<T::AccountId>>,484 PhantomType<RmrkInstanceInfo<T::AccountId>>,485 PhantomType<RmrkResourceInfo>,486 PhantomType<RmrkPropertyInfo>,487 PhantomType<RmrkBaseInfo<T::AccountId>>,488 PhantomType<RmrkPartType>,489 PhantomType<RmrkTheme>,490 PhantomType<RmrkNftChild>,491 ),492 QueryKind = OptionQuery,493 >;494495 #[pallet::hooks]496 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {497 fn on_runtime_upgrade() -> Weight {498 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {499 use up_data_structs::{CollectionVersion1, CollectionVersion2};500 <CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {501 Self::set_field_raw(502 id,503 CollectionField::OffchainSchema,504 v.offchain_schema.clone().into_inner(),505 )506 .expect("data has lower bounds than field");507 Self::set_field_raw(508 id,509 CollectionField::ConstOnChainSchema,510 v.const_on_chain_schema.clone().into_inner(),511 )512 .expect("data has lower bounds than field");513514 Some(CollectionVersion2::from(v))515 });516 }517518 0519 }520 }521}522523impl<T: Config> Pallet<T> {524 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens525 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {526 ensure!(527 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,528 <Error<T>>::AddressIsZero529 );530 Ok(())531 }532 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {533 <IsAdmin<T>>::iter_prefix((collection,))534 .map(|(a, _)| a)535 .collect()536 }537 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {538 <Allowlist<T>>::iter_prefix((collection,))539 .map(|(a, _)| a)540 .collect()541 }542 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {543 <Allowlist<T>>::get((collection, user))544 }545 pub fn collection_stats() -> CollectionStats {546 let created = <CreatedCollectionCount<T>>::get();547 let destroyed = <DestroyedCollectionCount<T>>::get();548 CollectionStats {549 created: created.0,550 destroyed: destroyed.0,551 alive: created.0 - destroyed.0,552 }553 }554555 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {556 let collection = <CollectionById<T>>::get(collection);557 if collection.is_none() {558 return None;559 }560561 let collection = collection.unwrap();562 let limits = collection.limits;563 let effective_limits = CollectionLimits {564 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),565 sponsored_data_size: Some(limits.sponsored_data_size()),566 sponsored_data_rate_limit: Some(567 limits568 .sponsored_data_rate_limit569 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),570 ),571 token_limit: Some(limits.token_limit()),572 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(573 match collection.mode {574 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,575 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,576 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,577 },578 )),579 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),580 owner_can_transfer: Some(limits.owner_can_transfer()),581 owner_can_destroy: Some(limits.owner_can_destroy()),582 transfers_enabled: Some(limits.transfers_enabled()),583 nesting_rule: Some(limits.nesting_rule().clone()),584 };585586 Some(effective_limits)587 }588589 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {590 let Collection {591 name,592 description,593 owner,594 mode,595 access,596 token_prefix,597 mint_mode,598 schema_version,599 sponsorship,600 limits,601 } = <CollectionById<T>>::get(collection)?;602603 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)604 .iter()605 .map(|(key, permission)| PropertyKeyPermission {606 key: key.clone(),607 permission: permission.clone(),608 })609 .collect();610611 let properties = <CollectionProperties<T>>::get(collection)612 .iter()613 .map(|(key, value)| Property {614 key: key.clone(),615 value: value.clone(),616 })617 .collect();618619 Some(RpcCollection {620 name: name.into_inner(),621 description: description.into_inner(),622 owner,623 mode,624 access,625 token_prefix: token_prefix.into_inner(),626 mint_mode,627 schema_version,628 sponsorship,629 limits,630 offchain_schema: <CollectionData<T>>::get((631 collection,632 CollectionField::OffchainSchema,633 ))634 .into_inner(),635 const_on_chain_schema: <CollectionData<T>>::get((636 collection,637 CollectionField::ConstOnChainSchema,638 ))639 .into_inner(),640 token_property_permissions,641 properties,642 })643 }644}645646impl<T: Config> Pallet<T> {647 pub fn init_collection(648 owner: T::AccountId,649 data: CreateCollectionData<T::AccountId>,650 ) -> Result<CollectionId, DispatchError> {651 {652 ensure!(653 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,654 Error::<T>::CollectionTokenPrefixLimitExceeded655 );656 }657658 let created_count = <CreatedCollectionCount<T>>::get()659 .0660 .checked_add(1)661 .ok_or(ArithmeticError::Overflow)?;662 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;663 let id = CollectionId(created_count);664665 // bound Total number of collections666 ensure!(667 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,668 <Error<T>>::TotalCollectionsLimitExceeded669 );670671 // =========672673 let collection = Collection {674 owner: owner.clone(),675 name: data.name,676 mode: data.mode.clone(),677 mint_mode: false,678 access: data.access.unwrap_or_default(),679 description: data.description,680 token_prefix: data.token_prefix,681 schema_version: data.schema_version.unwrap_or_default(),682 sponsorship: data683 .pending_sponsor684 .map(SponsorshipState::Unconfirmed)685 .unwrap_or_default(),686 limits: data687 .limits688 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))689 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,690 };691692 let mut collection_properties = up_data_structs::CollectionProperties::get();693 collection_properties694 .try_set_from_iter(data.properties.into_iter())695 .map_err(<Error<T>>::from)?;696697 CollectionProperties::<T>::insert(id, collection_properties);698699 let mut token_props_permissions = PropertiesPermissionMap::new();700 token_props_permissions701 .try_set_from_iter(data.token_property_permissions.into_iter())702 .map_err(<Error<T>>::from)?;703704 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);705706 // Take a (non-refundable) deposit of collection creation707 {708 let mut imbalance =709 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();710 imbalance.subsume(711 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(712 &T::TreasuryAccountId::get(),713 T::CollectionCreationPrice::get(),714 ),715 );716 <T as Config>::Currency::settle(717 &owner,718 imbalance,719 WithdrawReasons::TRANSFER,720 ExistenceRequirement::KeepAlive,721 )722 .map_err(|_| Error::<T>::NotSufficientFounds)?;723 }724725 <CreatedCollectionCount<T>>::put(created_count);726 <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));727 <CollectionById<T>>::insert(id, collection);728 Self::set_field_raw(729 id,730 CollectionField::OffchainSchema,731 data.offchain_schema.into_inner(),732 )733 .expect("data has lower bounds than field");734 Self::set_field_raw(735 id,736 CollectionField::ConstOnChainSchema,737 data.const_on_chain_schema.into_inner(),738 )739 .expect("data has lower bounds than field");740 Ok(id)741 }742743 pub fn destroy_collection(744 collection: CollectionHandle<T>,745 sender: &T::CrossAccountId,746 ) -> DispatchResult {747 ensure!(748 collection.limits.owner_can_destroy(),749 <Error<T>>::NoPermission,750 );751 collection.check_is_owner(sender)?;752753 let destroyed_collections = <DestroyedCollectionCount<T>>::get()754 .0755 .checked_add(1)756 .ok_or(ArithmeticError::Overflow)?;757758 // =========759760 <DestroyedCollectionCount<T>>::put(destroyed_collections);761 <CollectionById<T>>::remove(collection.id);762 <CollectionData<T>>::remove_prefix((collection.id,), None);763 <AdminAmount<T>>::remove(collection.id);764 <IsAdmin<T>>::remove_prefix((collection.id,), None);765 <Allowlist<T>>::remove_prefix((collection.id,), None);766 <CollectionProperties<T>>::remove(collection.id);767768 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));769 Ok(())770 }771772 pub fn set_collection_property(773 collection: &CollectionHandle<T>,774 sender: &T::CrossAccountId,775 property: Property,776 ) -> DispatchResult {777 collection.check_is_owner_or_admin(sender)?;778779 CollectionProperties::<T>::try_mutate(collection.id, |properties| {780 let property = property.clone();781 properties.try_set(property.key, property.value)782 })783 .map_err(<Error<T>>::from)?;784785 Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));786787 Ok(())788 }789790 pub fn set_scoped_collection_property(791 collection: &CollectionHandle<T>,792 scope: PropertyScope,793 property: Property,794 ) -> DispatchResult {795 CollectionProperties::<T>::try_mutate(collection.id, |properties| {796 properties.try_scoped_set(scope, property.key, property.value)797 })798 .map_err(<Error<T>>::from)?;799800 Ok(())801 }802803 #[transactional]804 pub fn set_scoped_collection_properties(805 collection: &CollectionHandle<T>,806 scope: PropertyScope,807 properties: impl Iterator<Item=Property>,808 ) -> DispatchResult {809 CollectionProperties::<T>::try_mutate(collection.id, |stored_properties| {810 stored_properties.try_scoped_set_from_iter(scope, properties)811 })812 .map_err(<Error<T>>::from)?;813814 Ok(())815 }816817 #[transactional]818 pub fn set_collection_properties(819 collection: &CollectionHandle<T>,820 sender: &T::CrossAccountId,821 properties: Vec<Property>,822 ) -> DispatchResult {823 for property in properties {824 Self::set_collection_property(collection, sender, property)?;825 }826827 Ok(())828 }829830 pub fn delete_collection_property(831 collection: &CollectionHandle<T>,832 sender: &T::CrossAccountId,833 property_key: PropertyKey,834 ) -> DispatchResult {835 collection.check_is_owner_or_admin(sender)?;836837 CollectionProperties::<T>::try_mutate(collection.id, |properties| {838 properties.remove(&property_key)839 })840 .map_err(<Error<T>>::from)?;841842 Self::deposit_event(Event::CollectionPropertyDeleted(843 collection.id,844 property_key,845 ));846847 Ok(())848 }849850 #[transactional]851 pub fn delete_collection_properties(852 collection: &CollectionHandle<T>,853 sender: &T::CrossAccountId,854 property_keys: Vec<PropertyKey>,855 ) -> DispatchResult {856 for key in property_keys {857 Self::delete_collection_property(collection, sender, key)?;858 }859860 Ok(())861 }862863 pub fn set_property_permission(864 collection: &CollectionHandle<T>,865 sender: &T::CrossAccountId,866 property_permission: PropertyKeyPermission,867 ) -> DispatchResult {868 collection.check_is_owner_or_admin(sender)?;869870 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);871 let current_permission = all_permissions.get(&property_permission.key);872 if matches![873 current_permission,874 Some(PropertyPermission { mutable: false, .. })875 ] {876 return Err(<Error<T>>::NoPermission.into());877 }878879 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {880 let property_permission = property_permission.clone();881 permissions.try_set(property_permission.key, property_permission.permission)882 })883 .map_err(<Error<T>>::from)?;884885 Self::deposit_event(Event::PropertyPermissionSet(886 collection.id,887 property_permission.key,888 ));889890 Ok(())891 }892893 #[transactional]894 pub fn set_property_permissions(895 collection: &CollectionHandle<T>,896 sender: &T::CrossAccountId,897 property_permissions: Vec<PropertyKeyPermission>,898 ) -> DispatchResult {899 for prop_pemission in property_permissions {900 Self::set_property_permission(collection, sender, prop_pemission)?;901 }902903 Ok(())904 }905906 pub fn get_collection_property(collection_id: CollectionId, key: &PropertyKey) -> Option<PropertyValue> {907 Self::collection_properties(collection_id)908 .get(key)909 .cloned()910 }911912 pub fn bytes_keys_to_property_keys(913 keys: Vec<Vec<u8>>,914 ) -> Result<Vec<PropertyKey>, DispatchError> {915 keys.into_iter()916 .map(|key| -> Result<PropertyKey, DispatchError> {917 key.try_into()918 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())919 })920 .collect::<Result<Vec<PropertyKey>, DispatchError>>()921 }922923 pub fn filter_collection_properties(924 collection_id: CollectionId,925 keys: Option<Vec<PropertyKey>>,926 ) -> Result<Vec<Property>, DispatchError> {927 let properties = Self::collection_properties(collection_id);928929 let properties = keys930 .map(|keys| {931 keys.into_iter()932 .filter_map(|key| {933 properties.get(&key).map(|value| Property {934 key,935 value: value.clone(),936 })937 })938 .collect()939 })940 .unwrap_or_else(|| {941 properties942 .iter()943 .map(|(key, value)| Property {944 key: key.clone(),945 value: value.clone(),946 })947 .collect()948 });949950 Ok(properties)951 }952953 pub fn filter_property_permissions(954 collection_id: CollectionId,955 keys: Option<Vec<PropertyKey>>,956 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {957 let permissions = Self::property_permissions(collection_id);958959 let key_permissions = keys960 .map(|keys| {961 keys.into_iter()962 .filter_map(|key| {963 permissions964 .get(&key)965 .map(|permission| PropertyKeyPermission {966 key,967 permission: permission.clone(),968 })969 })970 .collect()971 })972 .unwrap_or_else(|| {973 permissions974 .iter()975 .map(|(key, permission)| PropertyKeyPermission {976 key: key.clone(),977 permission: permission.clone(),978 })979 .collect()980 });981982 Ok(key_permissions)983 }984985 fn set_field_raw(986 collection_id: CollectionId,987 field: CollectionField,988 value: Vec<u8>,989 ) -> DispatchResult {990 if !value.is_empty() {991 <CollectionData<T>>::insert(992 (collection_id, field),993 BoundedVec::try_from(value).map_err(|_| <Error<T>>::CollectionFieldSizeExceeded)?,994 )995 } else {996 <CollectionData<T>>::remove((collection_id, field));997 }998 Ok(())999 }10001001 pub fn set_field(1002 collection: &CollectionHandle<T>,1003 sender: &T::CrossAccountId,1004 field: CollectionField,1005 value: Vec<u8>,1006 ) -> DispatchResult {1007 collection.check_is_owner_or_admin(sender)?;10081009 // =========10101011 Self::set_field_raw(collection.id, field, value)1012 }10131014 pub fn toggle_allowlist(1015 collection: &CollectionHandle<T>,1016 sender: &T::CrossAccountId,1017 user: &T::CrossAccountId,1018 allowed: bool,1019 ) -> DispatchResult {1020 collection.check_is_owner_or_admin(sender)?;10211022 // =========10231024 if allowed {1025 <Allowlist<T>>::insert((collection.id, user), true);1026 } else {1027 <Allowlist<T>>::remove((collection.id, user));1028 }10291030 Ok(())1031 }10321033 pub fn toggle_admin(1034 collection: &CollectionHandle<T>,1035 sender: &T::CrossAccountId,1036 user: &T::CrossAccountId,1037 admin: bool,1038 ) -> DispatchResult {1039 collection.check_is_owner_or_admin(sender)?;10401041 let was_admin = <IsAdmin<T>>::get((collection.id, user));1042 if was_admin == admin {1043 return Ok(());1044 }1045 let amount = <AdminAmount<T>>::get(collection.id);10461047 if admin {1048 let amount = amount1049 .checked_add(1)1050 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1051 ensure!(1052 amount <= Self::collection_admins_limit(),1053 <Error<T>>::CollectionAdminCountExceeded,1054 );10551056 // =========10571058 <AdminAmount<T>>::insert(collection.id, amount);1059 <IsAdmin<T>>::insert((collection.id, user), true);1060 } else {1061 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1062 <IsAdmin<T>>::remove((collection.id, user));1063 }10641065 Ok(())1066 }10671068 pub fn clamp_limits(1069 mode: CollectionMode,1070 old_limit: &CollectionLimits,1071 mut new_limit: CollectionLimits,1072 ) -> Result<CollectionLimits, DispatchError> {1073 macro_rules! limit_default {1074 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1075 $(1076 if let Some($new) = $new.$field {1077 let $old = $old.$field($($arg)?);1078 let _ = $new;1079 let _ = $old;1080 $check1081 } else {1082 $new.$field = $old.$field1083 }1084 )*1085 }};1086 }10871088 limit_default!(old_limit, new_limit,1089 account_token_ownership_limit => ensure!(1090 new_limit <= MAX_TOKEN_OWNERSHIP,1091 <Error<T>>::CollectionLimitBoundsExceeded,1092 ),1093 sponsor_transfer_timeout(match mode {1094 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1095 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1096 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1097 }) => ensure!(1098 new_limit <= MAX_SPONSOR_TIMEOUT,1099 <Error<T>>::CollectionLimitBoundsExceeded,1100 ),1101 sponsored_data_size => ensure!(1102 new_limit <= CUSTOM_DATA_LIMIT,1103 <Error<T>>::CollectionLimitBoundsExceeded,1104 ),1105 token_limit => ensure!(1106 old_limit >= new_limit && new_limit > 0,1107 <Error<T>>::CollectionTokenLimitExceeded1108 ),1109 owner_can_transfer => ensure!(1110 old_limit || !new_limit,1111 <Error<T>>::OwnerPermissionsCantBeReverted,1112 ),1113 owner_can_destroy => ensure!(1114 old_limit || !new_limit,1115 <Error<T>>::OwnerPermissionsCantBeReverted,1116 ),1117 sponsored_data_rate_limit => {},1118 transfers_enabled => {},1119 );1120 Ok(new_limit)1121 }1122}11231124#[macro_export]1125macro_rules! unsupported {1126 () => {1127 Err(<Error<T>>::UnsupportedOperation.into())1128 };1129}11301131/// Worst cases1132pub trait CommonWeightInfo<CrossAccountId> {1133 fn create_item() -> Weight;1134 fn create_multiple_items(amount: u32) -> Weight;1135 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;1136 fn burn_item() -> Weight;1137 fn set_collection_properties(amount: u32) -> Weight;1138 fn delete_collection_properties(amount: u32) -> Weight;1139 fn set_token_properties(amount: u32) -> Weight;1140 fn delete_token_properties(amount: u32) -> Weight;1141 fn set_property_permissions(amount: u32) -> Weight;1142 fn transfer() -> Weight;1143 fn approve() -> Weight;1144 fn transfer_from() -> Weight;1145 fn burn_from() -> Weight;1146}11471148pub trait CommonCollectionOperations<T: Config> {1149 fn create_item(1150 &self,1151 sender: T::CrossAccountId,1152 to: T::CrossAccountId,1153 data: CreateItemData,1154 nesting_budget: &dyn Budget,1155 ) -> DispatchResultWithPostInfo;1156 fn create_multiple_items(1157 &self,1158 sender: T::CrossAccountId,1159 to: T::CrossAccountId,1160 data: Vec<CreateItemData>,1161 nesting_budget: &dyn Budget,1162 ) -> DispatchResultWithPostInfo;1163 fn create_multiple_items_ex(1164 &self,1165 sender: T::CrossAccountId,1166 data: CreateItemExData<T::CrossAccountId>,1167 nesting_budget: &dyn Budget,1168 ) -> DispatchResultWithPostInfo;1169 fn burn_item(1170 &self,1171 sender: T::CrossAccountId,1172 token: TokenId,1173 amount: u128,1174 ) -> DispatchResultWithPostInfo;1175 fn set_collection_properties(1176 &self,1177 sender: T::CrossAccountId,1178 properties: Vec<Property>,1179 ) -> DispatchResultWithPostInfo;1180 fn delete_collection_properties(1181 &self,1182 sender: &T::CrossAccountId,1183 property_keys: Vec<PropertyKey>,1184 ) -> DispatchResultWithPostInfo;1185 fn set_token_properties(1186 &self,1187 sender: T::CrossAccountId,1188 token_id: TokenId,1189 property: Vec<Property>,1190 ) -> DispatchResultWithPostInfo;1191 fn delete_token_properties(1192 &self,1193 sender: T::CrossAccountId,1194 token_id: TokenId,1195 property_keys: Vec<PropertyKey>,1196 ) -> DispatchResultWithPostInfo;1197 fn set_property_permissions(1198 &self,1199 sender: &T::CrossAccountId,1200 property_permissions: Vec<PropertyKeyPermission>,1201 ) -> DispatchResultWithPostInfo;1202 fn transfer(1203 &self,1204 sender: T::CrossAccountId,1205 to: T::CrossAccountId,1206 token: TokenId,1207 amount: u128,1208 nesting_budget: &dyn Budget,1209 ) -> DispatchResultWithPostInfo;1210 fn approve(1211 &self,1212 sender: T::CrossAccountId,1213 spender: T::CrossAccountId,1214 token: TokenId,1215 amount: u128,1216 ) -> DispatchResultWithPostInfo;1217 fn transfer_from(1218 &self,1219 sender: T::CrossAccountId,1220 from: T::CrossAccountId,1221 to: T::CrossAccountId,1222 token: TokenId,1223 amount: u128,1224 nesting_budget: &dyn Budget,1225 ) -> DispatchResultWithPostInfo;1226 fn burn_from(1227 &self,1228 sender: T::CrossAccountId,1229 from: T::CrossAccountId,1230 token: TokenId,1231 amount: u128,1232 nesting_budget: &dyn Budget,1233 ) -> DispatchResultWithPostInfo;12341235 fn check_nesting(1236 &self,1237 sender: T::CrossAccountId,1238 from: (CollectionId, TokenId),1239 under: TokenId,1240 budget: &dyn Budget,1241 ) -> DispatchResult;12421243 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1244 fn collection_tokens(&self) -> Vec<TokenId>;1245 fn token_exists(&self, token: TokenId) -> bool;1246 fn last_token_id(&self) -> TokenId;12471248 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1249 fn const_metadata(&self, token: TokenId) -> Vec<u8>;1250 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1251 fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1252 /// Amount of unique collection tokens1253 fn total_supply(&self) -> u32;1254 /// Amount of different tokens account has (Applicable to nonfungible/refungible)1255 fn account_balance(&self, account: T::CrossAccountId) -> u32;1256 /// Amount of specific token account have (Applicable to fungible/refungible)1257 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1258 fn allowance(1259 &self,1260 sender: T::CrossAccountId,1261 spender: T::CrossAccountId,1262 token: TokenId,1263 ) -> u128;1264}12651266// Flexible enough for implementing CommonCollectionOperations1267pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1268 let post_info = PostDispatchInfo {1269 actual_weight: Some(weight),1270 pays_fee: Pays::Yes,1271 };1272 match res {1273 Ok(()) => Ok(post_info),1274 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1275 }1276}12771278impl<T: Config> From<PropertiesError> for Error<T> {1279 fn from(error: PropertiesError) -> Self {1280 match error {1281 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1282 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1283 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1284 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1285 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1286 }1287 }1288}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#![cfg_attr(not(feature = "std"), no_std)]1819extern crate alloc;2021use core::ops::{Deref, DerefMut};22use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};23use sp_std::vec::Vec;24use pallet_evm::account::CrossAccountId;25use frame_support::{26 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Weight, PostDispatchInfo},27 ensure,28 traits::{Imbalance, Get, Currency, WithdrawReasons, ExistenceRequirement},29 BoundedVec,30 weights::Pays,31 transactional,32};33use pallet_evm::GasWeightMapping;34use up_data_structs::{35 COLLECTION_NUMBER_LIMIT,36 Collection,37 RpcCollection,38 CollectionId,39 CreateItemData,40 MAX_TOKEN_PREFIX_LENGTH,41 COLLECTION_ADMINS_LIMIT,42 TokenId,43 CollectionStats,44 MAX_TOKEN_OWNERSHIP,45 CollectionMode,46 NFT_SPONSOR_TRANSFER_TIMEOUT,47 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,48 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,49 MAX_SPONSOR_TIMEOUT,50 CUSTOM_DATA_LIMIT,51 CollectionLimits,52 CreateCollectionData,53 SponsorshipState,54 CreateItemExData,55 SponsoringRateLimit,56 budget::Budget,57 COLLECTION_FIELD_LIMIT,58 CollectionField,59 PhantomType,60 Property,61 Properties,62 PropertiesPermissionMap,63 PropertyKey,64 PropertyValue,65 PropertyPermission,66 PropertiesError,67 PropertyKeyPermission,68 TokenData,69 TrySetProperty,70 PropertyScope,71 // RMRK72 RmrkCollectionInfo,73 RmrkInstanceInfo,74 RmrkResourceInfo,75 RmrkPropertyInfo,76 RmrkBaseInfo,77 RmrkPartType,78 RmrkTheme,79 RmrkNftChild,80};8182pub use pallet::*;83use sp_core::H160;84use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};85#[cfg(feature = "runtime-benchmarks")]86pub mod benchmarking;87pub mod dispatch;88pub mod erc;89pub mod eth;90pub mod weights;9192pub type SelfWeightOf<T> = <T as Config>::WeightInfo;9394#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]95pub struct CollectionHandle<T: Config> {96 pub id: CollectionId,97 collection: Collection<T::AccountId>,98 pub recorder: SubstrateRecorder<T>,99}100impl<T: Config> WithRecorder<T> for CollectionHandle<T> {101 fn recorder(&self) -> &SubstrateRecorder<T> {102 &self.recorder103 }104 fn into_recorder(self) -> SubstrateRecorder<T> {105 self.recorder106 }107}108impl<T: Config> CollectionHandle<T> {109 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {110 <CollectionById<T>>::get(id).map(|collection| Self {111 id,112 collection,113 recorder: SubstrateRecorder::new(gas_limit),114 })115 }116 pub fn new(id: CollectionId) -> Option<Self> {117 Self::new_with_gas_limit(id, u64::MAX)118 }119 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {120 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)121 }122 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {123 self.recorder124 .consume_gas(T::GasWeightMapping::weight_to_gas(125 <T as frame_system::Config>::DbWeight::get()126 .read127 .saturating_mul(reads),128 ))129 }130 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {131 self.recorder132 .consume_gas(T::GasWeightMapping::weight_to_gas(133 <T as frame_system::Config>::DbWeight::get()134 .write135 .saturating_mul(writes),136 ))137 }138 pub fn save(self) -> DispatchResult {139 <CollectionById<T>>::insert(self.id, self.collection);140 Ok(())141 }142}143impl<T: Config> Deref for CollectionHandle<T> {144 type Target = Collection<T::AccountId>;145146 fn deref(&self) -> &Self::Target {147 &self.collection148 }149}150151impl<T: Config> DerefMut for CollectionHandle<T> {152 fn deref_mut(&mut self) -> &mut Self::Target {153 &mut self.collection154 }155}156157impl<T: Config> CollectionHandle<T> {158 pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {159 ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);160 Ok(())161 }162 pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {163 *subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))164 }165 pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {166 ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);167 Ok(())168 }169 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {170 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)171 }172 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {173 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)174 }175 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {176 ensure!(177 <Allowlist<T>>::get((self.id, user)),178 <Error<T>>::AddressNotInAllowlist179 );180 Ok(())181 }182}183184#[frame_support::pallet]185pub mod pallet {186 use super::*;187 use pallet_evm::account;188 use dispatch::CollectionDispatch;189 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key, traits::StorageVersion};190 use frame_system::pallet_prelude::*;191 use frame_support::traits::Currency;192 use up_data_structs::{TokenId, mapping::TokenAddressMapping};193 use scale_info::TypeInfo;194 use weights::WeightInfo;195196 #[pallet::config]197 pub trait Config:198 frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config199 {200 type WeightInfo: WeightInfo;201 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;202203 type Currency: Currency<Self::AccountId>;204205 #[pallet::constant]206 type CollectionCreationPrice: Get<207 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,208 >;209 type CollectionDispatch: CollectionDispatch<Self>;210211 type TreasuryAccountId: Get<Self::AccountId>;212213 type EvmTokenAddressMapping: TokenAddressMapping<H160>;214 type CrossTokenAddressMapping: TokenAddressMapping<Self::CrossAccountId>;215 }216217 const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);218219 #[pallet::pallet]220 #[pallet::storage_version(STORAGE_VERSION)]221 #[pallet::generate_store(pub(super) trait Store)]222 pub struct Pallet<T>(_);223224 #[pallet::extra_constants]225 impl<T: Config> Pallet<T> {226 pub fn collection_admins_limit() -> u32 {227 COLLECTION_ADMINS_LIMIT228 }229 }230231 #[pallet::event]232 #[pallet::generate_deposit(pub fn deposit_event)]233 pub enum Event<T: Config> {234 /// New collection was created235 ///236 /// # Arguments237 ///238 /// * collection_id: Globally unique identifier of newly created collection.239 ///240 /// * mode: [CollectionMode] converted into u8.241 ///242 /// * account_id: Collection owner.243 CollectionCreated(CollectionId, u8, T::AccountId),244245 /// New collection was destroyed246 ///247 /// # Arguments248 ///249 /// * collection_id: Globally unique identifier of collection.250 CollectionDestroyed(CollectionId),251252 /// New item was created.253 ///254 /// # Arguments255 ///256 /// * collection_id: Id of the collection where item was created.257 ///258 /// * item_id: Id of an item. Unique within the collection.259 ///260 /// * recipient: Owner of newly created item261 ///262 /// * amount: Always 1 for NFT263 ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),264265 /// Collection item was burned.266 ///267 /// # Arguments268 ///269 /// * collection_id.270 ///271 /// * item_id: Identifier of burned NFT.272 ///273 /// * owner: which user has destroyed its tokens274 ///275 /// * amount: Always 1 for NFT276 ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),277278 /// Item was transferred279 ///280 /// * collection_id: Id of collection to which item is belong281 ///282 /// * item_id: Id of an item283 ///284 /// * sender: Original owner of item285 ///286 /// * recipient: New owner of item287 ///288 /// * amount: Always 1 for NFT289 Transfer(290 CollectionId,291 TokenId,292 T::CrossAccountId,293 T::CrossAccountId,294 u128,295 ),296297 /// * collection_id298 ///299 /// * item_id300 ///301 /// * sender302 ///303 /// * spender304 ///305 /// * amount306 Approved(307 CollectionId,308 TokenId,309 T::CrossAccountId,310 T::CrossAccountId,311 u128,312 ),313314 CollectionPropertySet(CollectionId, PropertyKey),315316 CollectionPropertyDeleted(CollectionId, PropertyKey),317318 TokenPropertySet(CollectionId, TokenId, PropertyKey),319320 TokenPropertyDeleted(CollectionId, TokenId, PropertyKey),321322 PropertyPermissionSet(CollectionId, PropertyKey),323 }324325 #[pallet::error]326 pub enum Error<T> {327 /// This collection does not exist.328 CollectionNotFound,329 /// Sender parameter and item owner must be equal.330 MustBeTokenOwner,331 /// No permission to perform action332 NoPermission,333 /// Collection is not in mint mode.334 PublicMintingNotAllowed,335 /// Address is not in allow list.336 AddressNotInAllowlist,337338 /// Collection name can not be longer than 63 char.339 CollectionNameLimitExceeded,340 /// Collection description can not be longer than 255 char.341 CollectionDescriptionLimitExceeded,342 /// Token prefix can not be longer than 15 char.343 CollectionTokenPrefixLimitExceeded,344 /// Total collections bound exceeded.345 TotalCollectionsLimitExceeded,346 /// Exceeded max admin count347 CollectionAdminCountExceeded,348 /// Collection limit bounds per collection exceeded349 CollectionLimitBoundsExceeded,350 /// Tried to enable permissions which are only permitted to be disabled351 OwnerPermissionsCantBeReverted,352 /// Collection settings not allowing items transferring353 TransferNotAllowed,354 /// Account token limit exceeded per collection355 AccountTokenLimitExceeded,356 /// Collection token limit exceeded357 CollectionTokenLimitExceeded,358 /// Metadata flag frozen359 MetadataFlagFrozen,360361 /// Item not exists.362 TokenNotFound,363 /// Item balance not enough.364 TokenValueTooLow,365 /// Requested value more than approved.366 ApprovedValueTooLow,367 /// Tried to approve more than owned368 CantApproveMoreThanOwned,369370 /// Can't transfer tokens to ethereum zero address371 AddressIsZero,372 /// Target collection doesn't supports this operation373 UnsupportedOperation,374375 /// Not sufficient founds to perform action376 NotSufficientFounds,377378 /// Collection has nesting disabled379 NestingIsDisabled,380 /// Only owner may nest tokens under this collection381 OnlyOwnerAllowedToNest,382 /// Only tokens from specific collections may nest tokens under this383 SourceCollectionIsNotAllowedToNest,384385 /// Tried to store more data than allowed in collection field386 CollectionFieldSizeExceeded,387388 /// Tried to store more property data than allowed389 NoSpaceForProperty,390391 /// Tried to store more property keys than allowed392 PropertyLimitReached,393394 /// Property key is too long395 PropertyKeyIsTooLong,396397 /// Only ASCII letters, digits, and '_', '-' are allowed398 InvalidCharacterInPropertyKey,399400 /// Empty property keys are forbidden401 EmptyPropertyKey,402 }403404 #[pallet::storage]405 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;406 #[pallet::storage]407 pub type DestroyedCollectionCount<T> =408 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;409410 /// Collection info411 #[pallet::storage]412 pub type CollectionById<T> = StorageMap<413 Hasher = Blake2_128Concat,414 Key = CollectionId,415 Value = Collection<<T as frame_system::Config>::AccountId>,416 QueryKind = OptionQuery,417 >;418419 /// Collection properties420 #[pallet::storage]421 #[pallet::getter(fn collection_properties)]422 pub type CollectionProperties<T> = StorageMap<423 Hasher = Blake2_128Concat,424 Key = CollectionId,425 Value = Properties,426 QueryKind = ValueQuery,427 OnEmpty = up_data_structs::CollectionProperties,428 >;429430 #[pallet::storage]431 #[pallet::getter(fn property_permissions)]432 pub type CollectionPropertyPermissions<T> = StorageMap<433 Hasher = Blake2_128Concat,434 Key = CollectionId,435 Value = PropertiesPermissionMap,436 QueryKind = ValueQuery,437 >;438439 /// Large variable-size collection fields are extracted here440 #[pallet::storage]441 pub type CollectionData<T> = StorageNMap<442 Key = (443 Key<Twox64Concat, CollectionId>,444 Key<Twox64Concat, CollectionField>,445 ),446 Value = BoundedVec<u8, ConstU32<COLLECTION_FIELD_LIMIT>>,447 QueryKind = ValueQuery,448 >;449450 #[pallet::storage]451 pub type AdminAmount<T> = StorageMap<452 Hasher = Blake2_128Concat,453 Key = CollectionId,454 Value = u32,455 QueryKind = ValueQuery,456 >;457458 /// List of collection admins459 #[pallet::storage]460 pub type IsAdmin<T: Config> = StorageNMap<461 Key = (462 Key<Blake2_128Concat, CollectionId>,463 Key<Blake2_128Concat, T::CrossAccountId>,464 ),465 Value = bool,466 QueryKind = ValueQuery,467 >;468469 /// Allowlisted collection users470 #[pallet::storage]471 pub type Allowlist<T: Config> = StorageNMap<472 Key = (473 Key<Blake2_128Concat, CollectionId>,474 Key<Blake2_128Concat, T::CrossAccountId>,475 ),476 Value = bool,477 QueryKind = ValueQuery,478 >;479480 /// Not used by code, exists only to provide some types to metadata481 #[pallet::storage]482 pub type DummyStorageValue<T: Config> = StorageValue<483 Value = (484 CollectionStats,485 CollectionId,486 TokenId,487 PhantomType<TokenData<T::CrossAccountId>>,488 PhantomType<RpcCollection<T::AccountId>>,489 // RMRK490 PhantomType<RmrkCollectionInfo<T::AccountId>>,491 PhantomType<RmrkInstanceInfo<T::AccountId>>,492 PhantomType<RmrkResourceInfo>,493 PhantomType<RmrkPropertyInfo>,494 PhantomType<RmrkBaseInfo<T::AccountId>>,495 PhantomType<RmrkPartType>,496 PhantomType<RmrkTheme>,497 PhantomType<RmrkNftChild>,498 ),499 QueryKind = OptionQuery,500 >;501502 #[pallet::hooks]503 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {504 fn on_runtime_upgrade() -> Weight {505 if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {506 use up_data_structs::{CollectionVersion1, CollectionVersion2};507 <CollectionById<T>>::translate::<CollectionVersion1<T::AccountId>, _>(|id, v| {508 Self::set_field_raw(509 id,510 CollectionField::OffchainSchema,511 v.offchain_schema.clone().into_inner(),512 )513 .expect("data has lower bounds than field");514 Self::set_field_raw(515 id,516 CollectionField::ConstOnChainSchema,517 v.const_on_chain_schema.clone().into_inner(),518 )519 .expect("data has lower bounds than field");520521 Some(CollectionVersion2::from(v))522 });523 }524525 0526 }527 }528}529530impl<T: Config> Pallet<T> {531 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens532 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {533 ensure!(534 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,535 <Error<T>>::AddressIsZero536 );537 Ok(())538 }539 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {540 <IsAdmin<T>>::iter_prefix((collection,))541 .map(|(a, _)| a)542 .collect()543 }544 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {545 <Allowlist<T>>::iter_prefix((collection,))546 .map(|(a, _)| a)547 .collect()548 }549 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {550 <Allowlist<T>>::get((collection, user))551 }552 pub fn collection_stats() -> CollectionStats {553 let created = <CreatedCollectionCount<T>>::get();554 let destroyed = <DestroyedCollectionCount<T>>::get();555 CollectionStats {556 created: created.0,557 destroyed: destroyed.0,558 alive: created.0 - destroyed.0,559 }560 }561562 pub fn effective_collection_limits(collection: CollectionId) -> Option<CollectionLimits> {563 let collection = <CollectionById<T>>::get(collection);564 if collection.is_none() {565 return None;566 }567568 let collection = collection.unwrap();569 let limits = collection.limits;570 let effective_limits = CollectionLimits {571 account_token_ownership_limit: Some(limits.account_token_ownership_limit()),572 sponsored_data_size: Some(limits.sponsored_data_size()),573 sponsored_data_rate_limit: Some(574 limits575 .sponsored_data_rate_limit576 .unwrap_or(SponsoringRateLimit::SponsoringDisabled),577 ),578 token_limit: Some(limits.token_limit()),579 sponsor_transfer_timeout: Some(limits.sponsor_transfer_timeout(580 match collection.mode {581 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,582 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,583 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,584 },585 )),586 sponsor_approve_timeout: Some(limits.sponsor_approve_timeout()),587 owner_can_transfer: Some(limits.owner_can_transfer()),588 owner_can_destroy: Some(limits.owner_can_destroy()),589 transfers_enabled: Some(limits.transfers_enabled()),590 nesting_rule: Some(limits.nesting_rule().clone()),591 };592593 Some(effective_limits)594 }595596 pub fn rpc_collection(collection: CollectionId) -> Option<RpcCollection<T::AccountId>> {597 let Collection {598 name,599 description,600 owner,601 mode,602 access,603 token_prefix,604 mint_mode,605 schema_version,606 sponsorship,607 limits,608 } = <CollectionById<T>>::get(collection)?;609610 let token_property_permissions = <CollectionPropertyPermissions<T>>::get(collection)611 .iter()612 .map(|(key, permission)| PropertyKeyPermission {613 key: key.clone(),614 permission: permission.clone(),615 })616 .collect();617618 let properties = <CollectionProperties<T>>::get(collection)619 .iter()620 .map(|(key, value)| Property {621 key: key.clone(),622 value: value.clone(),623 })624 .collect();625626 Some(RpcCollection {627 name: name.into_inner(),628 description: description.into_inner(),629 owner,630 mode,631 access,632 token_prefix: token_prefix.into_inner(),633 mint_mode,634 schema_version,635 sponsorship,636 limits,637 offchain_schema: <CollectionData<T>>::get((638 collection,639 CollectionField::OffchainSchema,640 ))641 .into_inner(),642 const_on_chain_schema: <CollectionData<T>>::get((643 collection,644 CollectionField::ConstOnChainSchema,645 ))646 .into_inner(),647 token_property_permissions,648 properties,649 })650 }651}652653impl<T: Config> Pallet<T> {654 pub fn init_collection(655 owner: T::AccountId,656 data: CreateCollectionData<T::AccountId>,657 ) -> Result<CollectionId, DispatchError> {658 {659 ensure!(660 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH as usize,661 Error::<T>::CollectionTokenPrefixLimitExceeded662 );663 }664665 let created_count = <CreatedCollectionCount<T>>::get()666 .0667 .checked_add(1)668 .ok_or(ArithmeticError::Overflow)?;669 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;670 let id = CollectionId(created_count);671672 // bound Total number of collections673 ensure!(674 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,675 <Error<T>>::TotalCollectionsLimitExceeded676 );677678 // =========679680 let collection = Collection {681 owner: owner.clone(),682 name: data.name,683 mode: data.mode.clone(),684 mint_mode: false,685 access: data.access.unwrap_or_default(),686 description: data.description,687 token_prefix: data.token_prefix,688 schema_version: data.schema_version.unwrap_or_default(),689 sponsorship: data690 .pending_sponsor691 .map(SponsorshipState::Unconfirmed)692 .unwrap_or_default(),693 limits: data694 .limits695 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))696 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,697 };698699 let mut collection_properties = up_data_structs::CollectionProperties::get();700 collection_properties701 .try_set_from_iter(data.properties.into_iter())702 .map_err(<Error<T>>::from)?;703704 CollectionProperties::<T>::insert(id, collection_properties);705706 let mut token_props_permissions = PropertiesPermissionMap::new();707 token_props_permissions708 .try_set_from_iter(data.token_property_permissions.into_iter())709 .map_err(<Error<T>>::from)?;710711 CollectionPropertyPermissions::<T>::insert(id, token_props_permissions);712713 // Take a (non-refundable) deposit of collection creation714 {715 let mut imbalance =716 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();717 imbalance.subsume(718 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(719 &T::TreasuryAccountId::get(),720 T::CollectionCreationPrice::get(),721 ),722 );723 <T as Config>::Currency::settle(724 &owner,725 imbalance,726 WithdrawReasons::TRANSFER,727 ExistenceRequirement::KeepAlive,728 )729 .map_err(|_| Error::<T>::NotSufficientFounds)?;730 }731732 <CreatedCollectionCount<T>>::put(created_count);733 <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));734 <CollectionById<T>>::insert(id, collection);735 Self::set_field_raw(736 id,737 CollectionField::OffchainSchema,738 data.offchain_schema.into_inner(),739 )740 .expect("data has lower bounds than field");741 Self::set_field_raw(742 id,743 CollectionField::ConstOnChainSchema,744 data.const_on_chain_schema.into_inner(),745 )746 .expect("data has lower bounds than field");747 Ok(id)748 }749750 pub fn destroy_collection(751 collection: CollectionHandle<T>,752 sender: &T::CrossAccountId,753 ) -> DispatchResult {754 ensure!(755 collection.limits.owner_can_destroy(),756 <Error<T>>::NoPermission,757 );758 collection.check_is_owner(sender)?;759760 let destroyed_collections = <DestroyedCollectionCount<T>>::get()761 .0762 .checked_add(1)763 .ok_or(ArithmeticError::Overflow)?;764765 // =========766767 <DestroyedCollectionCount<T>>::put(destroyed_collections);768 <CollectionById<T>>::remove(collection.id);769 <CollectionData<T>>::remove_prefix((collection.id,), None);770 <AdminAmount<T>>::remove(collection.id);771 <IsAdmin<T>>::remove_prefix((collection.id,), None);772 <Allowlist<T>>::remove_prefix((collection.id,), None);773 <CollectionProperties<T>>::remove(collection.id);774775 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));776 Ok(())777 }778779 pub fn set_collection_property(780 collection: &CollectionHandle<T>,781 sender: &T::CrossAccountId,782 property: Property,783 ) -> DispatchResult {784 collection.check_is_owner_or_admin(sender)?;785786 CollectionProperties::<T>::try_mutate(collection.id, |properties| {787 let property = property.clone();788 properties.try_set(property.key, property.value)789 })790 .map_err(<Error<T>>::from)?;791792 Self::deposit_event(Event::CollectionPropertySet(collection.id, property.key));793794 Ok(())795 }796797 pub fn set_scoped_collection_property(798 collection: &CollectionHandle<T>,799 scope: PropertyScope,800 property: Property,801 ) -> DispatchResult {802 CollectionProperties::<T>::try_mutate(collection.id, |properties| {803 properties.try_scoped_set(scope, property.key, property.value)804 })805 .map_err(<Error<T>>::from)?;806807 Ok(())808 }809810 #[transactional]811 pub fn set_scoped_collection_properties(812 collection: &CollectionHandle<T>,813 scope: PropertyScope,814 properties: impl Iterator<Item = Property>,815 ) -> DispatchResult {816 CollectionProperties::<T>::try_mutate(collection.id, |stored_properties| {817 stored_properties.try_scoped_set_from_iter(scope, properties)818 })819 .map_err(<Error<T>>::from)?;820821 Ok(())822 }823824 #[transactional]825 pub fn set_collection_properties(826 collection: &CollectionHandle<T>,827 sender: &T::CrossAccountId,828 properties: Vec<Property>,829 ) -> DispatchResult {830 for property in properties {831 Self::set_collection_property(collection, sender, property)?;832 }833834 Ok(())835 }836837 pub fn delete_collection_property(838 collection: &CollectionHandle<T>,839 sender: &T::CrossAccountId,840 property_key: PropertyKey,841 ) -> DispatchResult {842 collection.check_is_owner_or_admin(sender)?;843844 CollectionProperties::<T>::try_mutate(collection.id, |properties| {845 properties.remove(&property_key)846 })847 .map_err(<Error<T>>::from)?;848849 Self::deposit_event(Event::CollectionPropertyDeleted(850 collection.id,851 property_key,852 ));853854 Ok(())855 }856857 #[transactional]858 pub fn delete_collection_properties(859 collection: &CollectionHandle<T>,860 sender: &T::CrossAccountId,861 property_keys: Vec<PropertyKey>,862 ) -> DispatchResult {863 for key in property_keys {864 Self::delete_collection_property(collection, sender, key)?;865 }866867 Ok(())868 }869870 pub fn set_property_permission(871 collection: &CollectionHandle<T>,872 sender: &T::CrossAccountId,873 property_permission: PropertyKeyPermission,874 ) -> DispatchResult {875 collection.check_is_owner_or_admin(sender)?;876877 let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);878 let current_permission = all_permissions.get(&property_permission.key);879 if matches![880 current_permission,881 Some(PropertyPermission { mutable: false, .. })882 ] {883 return Err(<Error<T>>::NoPermission.into());884 }885886 CollectionPropertyPermissions::<T>::try_mutate(collection.id, |permissions| {887 let property_permission = property_permission.clone();888 permissions.try_set(property_permission.key, property_permission.permission)889 })890 .map_err(<Error<T>>::from)?;891892 Self::deposit_event(Event::PropertyPermissionSet(893 collection.id,894 property_permission.key,895 ));896897 Ok(())898 }899900 #[transactional]901 pub fn set_property_permissions(902 collection: &CollectionHandle<T>,903 sender: &T::CrossAccountId,904 property_permissions: Vec<PropertyKeyPermission>,905 ) -> DispatchResult {906 for prop_pemission in property_permissions {907 Self::set_property_permission(collection, sender, prop_pemission)?;908 }909910 Ok(())911 }912913 pub fn get_collection_property(914 collection_id: CollectionId,915 key: &PropertyKey,916 ) -> Option<PropertyValue> {917 Self::collection_properties(collection_id).get(key).cloned()918 }919920 pub fn bytes_keys_to_property_keys(921 keys: Vec<Vec<u8>>,922 ) -> Result<Vec<PropertyKey>, DispatchError> {923 keys.into_iter()924 .map(|key| -> Result<PropertyKey, DispatchError> {925 key.try_into()926 .map_err(|_| <Error<T>>::PropertyKeyIsTooLong.into())927 })928 .collect::<Result<Vec<PropertyKey>, DispatchError>>()929 }930931 pub fn filter_collection_properties(932 collection_id: CollectionId,933 keys: Option<Vec<PropertyKey>>,934 ) -> Result<Vec<Property>, DispatchError> {935 let properties = Self::collection_properties(collection_id);936937 let properties = keys938 .map(|keys| {939 keys.into_iter()940 .filter_map(|key| {941 properties.get(&key).map(|value| Property {942 key,943 value: value.clone(),944 })945 })946 .collect()947 })948 .unwrap_or_else(|| {949 properties950 .iter()951 .map(|(key, value)| Property {952 key: key.clone(),953 value: value.clone(),954 })955 .collect()956 });957958 Ok(properties)959 }960961 pub fn filter_property_permissions(962 collection_id: CollectionId,963 keys: Option<Vec<PropertyKey>>,964 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {965 let permissions = Self::property_permissions(collection_id);966967 let key_permissions = keys968 .map(|keys| {969 keys.into_iter()970 .filter_map(|key| {971 permissions972 .get(&key)973 .map(|permission| PropertyKeyPermission {974 key,975 permission: permission.clone(),976 })977 })978 .collect()979 })980 .unwrap_or_else(|| {981 permissions982 .iter()983 .map(|(key, permission)| PropertyKeyPermission {984 key: key.clone(),985 permission: permission.clone(),986 })987 .collect()988 });989990 Ok(key_permissions)991 }992993 fn set_field_raw(994 collection_id: CollectionId,995 field: CollectionField,996 value: Vec<u8>,997 ) -> DispatchResult {998 if !value.is_empty() {999 <CollectionData<T>>::insert(1000 (collection_id, field),1001 BoundedVec::try_from(value).map_err(|_| <Error<T>>::CollectionFieldSizeExceeded)?,1002 )1003 } else {1004 <CollectionData<T>>::remove((collection_id, field));1005 }1006 Ok(())1007 }10081009 pub fn set_field(1010 collection: &CollectionHandle<T>,1011 sender: &T::CrossAccountId,1012 field: CollectionField,1013 value: Vec<u8>,1014 ) -> DispatchResult {1015 collection.check_is_owner_or_admin(sender)?;10161017 // =========10181019 Self::set_field_raw(collection.id, field, value)1020 }10211022 pub fn toggle_allowlist(1023 collection: &CollectionHandle<T>,1024 sender: &T::CrossAccountId,1025 user: &T::CrossAccountId,1026 allowed: bool,1027 ) -> DispatchResult {1028 collection.check_is_owner_or_admin(sender)?;10291030 // =========10311032 if allowed {1033 <Allowlist<T>>::insert((collection.id, user), true);1034 } else {1035 <Allowlist<T>>::remove((collection.id, user));1036 }10371038 Ok(())1039 }10401041 pub fn toggle_admin(1042 collection: &CollectionHandle<T>,1043 sender: &T::CrossAccountId,1044 user: &T::CrossAccountId,1045 admin: bool,1046 ) -> DispatchResult {1047 collection.check_is_owner_or_admin(sender)?;10481049 let was_admin = <IsAdmin<T>>::get((collection.id, user));1050 if was_admin == admin {1051 return Ok(());1052 }1053 let amount = <AdminAmount<T>>::get(collection.id);10541055 if admin {1056 let amount = amount1057 .checked_add(1)1058 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;1059 ensure!(1060 amount <= Self::collection_admins_limit(),1061 <Error<T>>::CollectionAdminCountExceeded,1062 );10631064 // =========10651066 <AdminAmount<T>>::insert(collection.id, amount);1067 <IsAdmin<T>>::insert((collection.id, user), true);1068 } else {1069 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));1070 <IsAdmin<T>>::remove((collection.id, user));1071 }10721073 Ok(())1074 }10751076 pub fn clamp_limits(1077 mode: CollectionMode,1078 old_limit: &CollectionLimits,1079 mut new_limit: CollectionLimits,1080 ) -> Result<CollectionLimits, DispatchError> {1081 macro_rules! limit_default {1082 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1083 $(1084 if let Some($new) = $new.$field {1085 let $old = $old.$field($($arg)?);1086 let _ = $new;1087 let _ = $old;1088 $check1089 } else {1090 $new.$field = $old.$field1091 }1092 )*1093 }};1094 }10951096 limit_default!(old_limit, new_limit,1097 account_token_ownership_limit => ensure!(1098 new_limit <= MAX_TOKEN_OWNERSHIP,1099 <Error<T>>::CollectionLimitBoundsExceeded,1100 ),1101 sponsor_transfer_timeout(match mode {1102 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1103 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1104 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1105 }) => ensure!(1106 new_limit <= MAX_SPONSOR_TIMEOUT,1107 <Error<T>>::CollectionLimitBoundsExceeded,1108 ),1109 sponsored_data_size => ensure!(1110 new_limit <= CUSTOM_DATA_LIMIT,1111 <Error<T>>::CollectionLimitBoundsExceeded,1112 ),1113 token_limit => ensure!(1114 old_limit >= new_limit && new_limit > 0,1115 <Error<T>>::CollectionTokenLimitExceeded1116 ),1117 owner_can_transfer => ensure!(1118 old_limit || !new_limit,1119 <Error<T>>::OwnerPermissionsCantBeReverted,1120 ),1121 owner_can_destroy => ensure!(1122 old_limit || !new_limit,1123 <Error<T>>::OwnerPermissionsCantBeReverted,1124 ),1125 sponsored_data_rate_limit => {},1126 transfers_enabled => {},1127 );1128 Ok(new_limit)1129 }1130}11311132#[macro_export]1133macro_rules! unsupported {1134 () => {1135 Err(<Error<T>>::UnsupportedOperation.into())1136 };1137}11381139/// Worst cases1140pub trait CommonWeightInfo<CrossAccountId> {1141 fn create_item() -> Weight;1142 fn create_multiple_items(amount: &[CreateItemData]) -> Weight;1143 fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;1144 fn burn_item() -> Weight;1145 fn set_collection_properties(amount: u32) -> Weight;1146 fn delete_collection_properties(amount: u32) -> Weight;1147 fn set_token_properties(amount: u32) -> Weight;1148 fn delete_token_properties(amount: u32) -> Weight;1149 fn set_property_permissions(amount: u32) -> Weight;1150 fn transfer() -> Weight;1151 fn approve() -> Weight;1152 fn transfer_from() -> Weight;1153 fn burn_from() -> Weight;1154}11551156pub trait CommonCollectionOperations<T: Config> {1157 fn create_item(1158 &self,1159 sender: T::CrossAccountId,1160 to: T::CrossAccountId,1161 data: CreateItemData,1162 nesting_budget: &dyn Budget,1163 ) -> DispatchResultWithPostInfo;1164 fn create_multiple_items(1165 &self,1166 sender: T::CrossAccountId,1167 to: T::CrossAccountId,1168 data: Vec<CreateItemData>,1169 nesting_budget: &dyn Budget,1170 ) -> DispatchResultWithPostInfo;1171 fn create_multiple_items_ex(1172 &self,1173 sender: T::CrossAccountId,1174 data: CreateItemExData<T::CrossAccountId>,1175 nesting_budget: &dyn Budget,1176 ) -> DispatchResultWithPostInfo;1177 fn burn_item(1178 &self,1179 sender: T::CrossAccountId,1180 token: TokenId,1181 amount: u128,1182 ) -> DispatchResultWithPostInfo;1183 fn set_collection_properties(1184 &self,1185 sender: T::CrossAccountId,1186 properties: Vec<Property>,1187 ) -> DispatchResultWithPostInfo;1188 fn delete_collection_properties(1189 &self,1190 sender: &T::CrossAccountId,1191 property_keys: Vec<PropertyKey>,1192 ) -> DispatchResultWithPostInfo;1193 fn set_token_properties(1194 &self,1195 sender: T::CrossAccountId,1196 token_id: TokenId,1197 property: Vec<Property>,1198 ) -> DispatchResultWithPostInfo;1199 fn delete_token_properties(1200 &self,1201 sender: T::CrossAccountId,1202 token_id: TokenId,1203 property_keys: Vec<PropertyKey>,1204 ) -> DispatchResultWithPostInfo;1205 fn set_property_permissions(1206 &self,1207 sender: &T::CrossAccountId,1208 property_permissions: Vec<PropertyKeyPermission>,1209 ) -> DispatchResultWithPostInfo;1210 fn transfer(1211 &self,1212 sender: T::CrossAccountId,1213 to: T::CrossAccountId,1214 token: TokenId,1215 amount: u128,1216 nesting_budget: &dyn Budget,1217 ) -> DispatchResultWithPostInfo;1218 fn approve(1219 &self,1220 sender: T::CrossAccountId,1221 spender: T::CrossAccountId,1222 token: TokenId,1223 amount: u128,1224 ) -> DispatchResultWithPostInfo;1225 fn transfer_from(1226 &self,1227 sender: T::CrossAccountId,1228 from: T::CrossAccountId,1229 to: T::CrossAccountId,1230 token: TokenId,1231 amount: u128,1232 nesting_budget: &dyn Budget,1233 ) -> DispatchResultWithPostInfo;1234 fn burn_from(1235 &self,1236 sender: T::CrossAccountId,1237 from: T::CrossAccountId,1238 token: TokenId,1239 amount: u128,1240 nesting_budget: &dyn Budget,1241 ) -> DispatchResultWithPostInfo;12421243 fn check_nesting(1244 &self,1245 sender: T::CrossAccountId,1246 from: (CollectionId, TokenId),1247 under: TokenId,1248 budget: &dyn Budget,1249 ) -> DispatchResult;12501251 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;1252 fn collection_tokens(&self) -> Vec<TokenId>;1253 fn token_exists(&self, token: TokenId) -> bool;1254 fn last_token_id(&self) -> TokenId;12551256 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;1257 fn const_metadata(&self, token: TokenId) -> Vec<u8>;1258 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue>;1259 fn token_properties(&self, token_id: TokenId, keys: Option<Vec<PropertyKey>>) -> Vec<Property>;1260 /// Amount of unique collection tokens1261 fn total_supply(&self) -> u32;1262 /// Amount of different tokens account has (Applicable to nonfungible/refungible)1263 fn account_balance(&self, account: T::CrossAccountId) -> u32;1264 /// Amount of specific token account have (Applicable to fungible/refungible)1265 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;1266 fn allowance(1267 &self,1268 sender: T::CrossAccountId,1269 spender: T::CrossAccountId,1270 token: TokenId,1271 ) -> u128;1272}12731274// Flexible enough for implementing CommonCollectionOperations1275pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {1276 let post_info = PostDispatchInfo {1277 actual_weight: Some(weight),1278 pays_fee: Pays::Yes,1279 };1280 match res {1281 Ok(()) => Ok(post_info),1282 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),1283 }1284}12851286impl<T: Config> From<PropertiesError> for Error<T> {1287 fn from(error: PropertiesError) -> Self {1288 match error {1289 PropertiesError::NoSpaceForProperty => Self::NoSpaceForProperty,1290 PropertiesError::PropertyLimitReached => Self::PropertyLimitReached,1291 PropertiesError::InvalidCharacterInPropertyKey => Self::InvalidCharacterInPropertyKey,1292 PropertiesError::PropertyKeyIsTooLong => Self::PropertyKeyIsTooLong,1293 PropertiesError::EmptyPropertyKey => Self::EmptyPropertyKey,1294 }1295 }1296}pallets/common/src/weights.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/weights.rs
@@ -0,0 +1,79 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_common
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 1, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-common
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=1
+// --heap-pages=4096
+// --output=./pallets/common/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_common.
+pub trait WeightInfo {
+ fn set_collection_properties(b: u32, ) -> Weight;
+ fn delete_collection_properties(b: u32, ) -> Weight;
+}
+
+/// Weights for pallet_common using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+ // Storage: Common CollectionProperties (r:1 w:1)
+ fn set_collection_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 142_818_000
+ .saturating_add((2_786_252_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionProperties (r:1 w:1)
+ fn delete_collection_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 101_087_000
+ .saturating_add((2_739_521_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ // Storage: Common CollectionProperties (r:1 w:1)
+ fn set_collection_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 142_818_000
+ .saturating_add((2_786_252_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionProperties (r:1 w:1)
+ fn delete_collection_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 101_087_000
+ .saturating_add((2_739_521_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+}
pallets/fungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -40,7 +40,7 @@
owner: sub; collection: collection(owner);
sender: cross_from_sub(owner); to: cross_sub;
};
- }: {<Pallet<T>>::create_item(&collection, &sender, (to, 200))?}
+ }: {<Pallet<T>>::create_item(&collection, &sender, (to, 200), &Unlimited)?}
create_multiple_items_ex {
let b in 0..MAX_ITEMS_PER_BATCH;
@@ -52,14 +52,14 @@
bench_init!(to: cross_sub(i););
(to, 200)
}).collect::<BTreeMap<_, _>>().try_into().unwrap();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
burn_item {
bench_init!{
owner: sub; collection: collection(owner);
owner: cross_from_sub; burner: cross_sub;
};
- <Pallet<T>>::create_item(&collection, &owner, (burner.clone(), 200))?;
+ <Pallet<T>>::create_item(&collection, &owner, (burner.clone(), 200), &Unlimited)?;
}: {<Pallet<T>>::burn(&collection, &burner, 100)?}
transfer {
@@ -67,15 +67,15 @@
owner: sub; collection: collection(owner);
owner: cross_from_sub; sender: cross_sub; to: cross_sub;
};
- <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &to, 200)?}
+ <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
+ }: {<Pallet<T>>::transfer(&collection, &sender, &to, 200, &Unlimited)?}
approve {
bench_init!{
owner: sub; collection: collection(owner);
owner: cross_from_sub; sender: cross_sub; spender: cross_sub;
};
- <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+ <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
}: {<Pallet<T>>::set_allowance(&collection, &sender, &spender, 100)?}
transfer_from {
@@ -83,7 +83,7 @@
owner: sub; collection: collection(owner);
owner: cross_from_sub; sender: cross_sub; spender: cross_sub; receiver: cross_sub;
};
- <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+ <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
<Pallet<T>>::set_allowance(&collection, &sender, &spender, 200)?;
}: {<Pallet<T>>::transfer_from(&collection, &spender, &sender, &receiver, 100, &Unlimited)?}
@@ -92,7 +92,7 @@
owner: sub; collection: collection(owner);
owner: cross_from_sub; sender: cross_sub; burner: cross_sub;
};
- <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200))?;
+ <Pallet<T>>::create_item(&collection, &owner, (sender.clone(), 200), &Unlimited)?;
<Pallet<T>>::set_allowance(&collection, &sender, &burner, 200)?;
}: {<Pallet<T>>::burn_from(&collection, &burner, &sender, 100, &Unlimited)?}
}
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -17,7 +17,7 @@
use core::marker::PhantomData;
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};
-use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget};
+use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::ArithmeticError;
use sp_std::{vec::Vec, vec};
@@ -33,7 +33,8 @@
<SelfWeightOf<T>>::create_item()
}
- fn create_multiple_items(_amount: u32) -> Weight {
+ fn create_multiple_items(_data: &[CreateItemData]) -> Weight {
+ // All items minted for the same user, so it works same as create_item
Self::create_item()
}
@@ -51,23 +52,28 @@
}
fn set_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::set_collection_properties(amount)
+ // Error
+ 0
}
fn delete_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::delete_collection_properties(amount)
+ // Error
+ 0
}
fn set_token_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::set_token_properties(amount)
+ // Error
+ 0
}
fn delete_token_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::delete_token_properties(amount)
+ // Error
+ 0
}
fn set_property_permissions(amount: u32) -> Weight {
- <SelfWeightOf<T>>::set_property_permissions(amount)
+ // Error
+ 0
}
fn transfer() -> Weight {
pallets/fungible/src/weights.rsdiffbeforeafterboth--- a/pallets/fungible/src/weights.rs
+++ b/pallets/fungible/src/weights.rs
@@ -35,11 +35,6 @@
fn create_item() -> Weight;
fn create_multiple_items_ex(b: u32, ) -> Weight;
fn burn_item() -> Weight;
- fn set_collection_properties(amount: u32) -> Weight;
- fn delete_collection_properties(amount: u32) -> Weight;
- fn set_token_properties(amount: u32) -> Weight;
- fn delete_token_properties(amount: u32) -> Weight;
- fn set_property_permissions(amount: u32) -> Weight;
fn transfer() -> Weight;
fn approve() -> Weight;
fn transfer_from() -> Weight;
@@ -73,33 +68,8 @@
(15_565_000 as Weight)
.saturating_add(T::DbWeight::get().reads(2 as Weight))
.saturating_add(T::DbWeight::get().writes(2 as Weight))
- }
-
- fn set_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn set_token_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_token_properties(_amount: u32) -> Weight {
- // Error
- 0
}
- fn set_property_permissions(_amount: u32) -> Weight {
- // Error
- 0
- }
-
// Storage: Fungible Balance (r:2 w:2)
fn transfer() -> Weight {
(17_713_000 as Weight)
@@ -156,31 +126,6 @@
(15_565_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(2 as Weight))
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
- }
-
- fn set_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn set_token_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_token_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn set_property_permissions(_amount: u32) -> Weight {
- // Error
- 0
}
// Storage: Fungible Balance (r:2 w:2)
pallets/nonfungible/Cargo.tomldiffbeforeafterboth--- a/pallets/nonfungible/Cargo.toml
+++ b/pallets/nonfungible/Cargo.toml
@@ -49,4 +49,5 @@
'frame-benchmarking',
'frame-support/runtime-benchmarks',
'frame-system/runtime-benchmarks',
+ 'up-data-structs/runtime-benchmarks',
]
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -18,24 +18,35 @@
use crate::{Pallet, Config, NonfungibleHandle};
use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data, property_key, property_value};
use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT, budget::Unlimited};
+use up_data_structs::{
+ CollectionMode, MAX_ITEMS_PER_BATCH, MAX_PROPERTIES_PER_ITEM, CUSTOM_DATA_LIMIT,
+ budget::Unlimited,
+};
use pallet_common::bench_init;
-use core::convert::TryInto;
const SEED: u32 = 1;
fn create_max_item_data<T: Config>(owner: T::CrossAccountId) -> CreateItemData<T> {
let const_data = create_data::<CUSTOM_DATA_LIMIT>();
- CreateItemData::<T> { const_data, owner }
+ CreateItemData::<T> {
+ const_data,
+ owner,
+ properties: Default::default(),
+ }
}
fn create_max_item<T: Config>(
collection: &NonfungibleHandle<T>,
sender: &T::CrossAccountId,
owner: T::CrossAccountId,
) -> Result<TokenId, DispatchError> {
- <Pallet<T>>::create_item(&collection, sender, create_max_item_data::<T>(owner))?;
+ <Pallet<T>>::create_item(
+ &collection,
+ sender,
+ create_max_item_data::<T>(owner),
+ &Unlimited,
+ )?;
Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
}
@@ -65,7 +76,7 @@
sender: cross_from_sub(owner); to: cross_sub;
};
let data = (0..b).map(|_| create_max_item_data::<T>(to.clone())).collect();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
create_multiple_items_ex {
let b in 0..MAX_ITEMS_PER_BATCH;
@@ -77,7 +88,7 @@
bench_init!(to: cross_sub(i););
create_max_item_data::<T>(to)
}).collect();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
burn_item {
bench_init!{
@@ -93,7 +104,7 @@
owner: cross_from_sub; sender: cross_sub; receiver: cross_sub;
};
let item = create_max_item(&collection, &owner, sender.clone())?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item)?}
+ }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, &Unlimited)?}
approve {
bench_init!{
@@ -120,4 +131,66 @@
let item = create_max_item(&collection, &owner, sender.clone())?;
<Pallet<T>>::set_allowance(&collection, &sender, item, Some(&burner))?;
}: {<Pallet<T>>::burn_from(&collection, &burner, &sender, item, &Unlimited)?}
+
+ set_property_permissions {
+ let b in 0..MAX_PROPERTIES_PER_ITEM;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let perms = (0..b).map(|k| PropertyKeyPermission {
+ key: property_key(k as usize),
+ permission: PropertyPermission {
+ mutable: false,
+ collection_admin: false,
+ token_owner: false,
+ },
+ }).collect::<Vec<_>>();
+ }: {<Pallet<T>>::set_property_permissions(&collection, &owner, perms)?}
+
+ set_token_properties {
+ let b in 0..MAX_PROPERTIES_PER_ITEM;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let perms = (0..b).map(|k| PropertyKeyPermission {
+ key: property_key(k as usize),
+ permission: PropertyPermission {
+ mutable: false,
+ collection_admin: true,
+ token_owner: true,
+ },
+ }).collect::<Vec<_>>();
+ <Pallet<T>>::set_property_permissions(&collection, &owner, perms)?;
+ let props = (0..b).map(|k| Property {
+ key: property_key(k as usize),
+ value: property_value(),
+ }).collect::<Vec<_>>();
+ let item = create_max_item(&collection, &owner, owner.clone())?;
+ }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?}
+
+ delete_token_properties {
+ let b in 0..MAX_PROPERTIES_PER_ITEM;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ owner: cross_from_sub;
+ };
+ let perms = (0..b).map(|k| PropertyKeyPermission {
+ key: property_key(k as usize),
+ permission: PropertyPermission {
+ mutable: true,
+ collection_admin: true,
+ token_owner: true,
+ },
+ }).collect::<Vec<_>>();
+ <Pallet<T>>::set_property_permissions(&collection, &owner, perms)?;
+ let props = (0..b).map(|k| Property {
+ key: property_key(k as usize),
+ value: property_value(),
+ }).collect::<Vec<_>>();
+ let item = create_max_item(&collection, &owner, owner.clone())?;
+ <Pallet<T>>::set_token_properties(&collection, &owner, item, props)?;
+ let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
+ }: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}
}
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -21,7 +21,9 @@
TokenId, CreateItemExData, CollectionId, budget::Budget, Property, PropertyKey,
PropertyKeyPermission, PropertyValue,
};
-use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
+use pallet_common::{
+ CommonCollectionOperations, CommonWeightInfo, with_weight, weights::WeightInfo as _,
+};
use sp_runtime::DispatchError;
use sp_std::vec::Vec;
@@ -38,13 +40,33 @@
fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
match data {
- CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32),
+ CreateItemExData::NFT(t) => {
+ <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32)
+ + t.iter()
+ .map(|t| {
+ if t.properties.len() > 0 {
+ Self::set_token_properties(t.properties.len() as u32)
+ } else {
+ 0
+ }
+ })
+ .sum::<u64>()
+ }
_ => 0,
}
}
- fn create_multiple_items(amount: u32) -> Weight {
- <SelfWeightOf<T>>::create_multiple_items(amount)
+ fn create_multiple_items(data: &[up_data_structs::CreateItemData]) -> Weight {
+ <SelfWeightOf<T>>::create_multiple_items(data.len() as u32)
+ + data
+ .iter()
+ .filter_map(|t| match t {
+ up_data_structs::CreateItemData::NFT(n) if n.properties.len() > 0 => {
+ Some(Self::set_token_properties(n.properties.len() as u32))
+ }
+ _ => None,
+ })
+ .sum::<u64>()
}
fn burn_item() -> Weight {
@@ -52,11 +74,11 @@
}
fn set_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::set_collection_properties(amount)
+ <pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)
}
fn delete_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::delete_collection_properties(amount)
+ <pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)
}
fn set_token_properties(amount: u32) -> Weight {
@@ -128,15 +150,15 @@
data: Vec<up_data_structs::CreateItemData>,
nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
+ let weight = <CommonWeights<T>>::create_multiple_items(&data);
let data = data
.into_iter()
.map(|d| map_create_data::<T>(d, &to))
.collect::<Result<Vec<_>, DispatchError>>()?;
- let amount = data.len();
with_weight(
<Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),
- <CommonWeights<T>>::create_multiple_items(amount as u32),
+ weight,
)
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -18,7 +18,7 @@
use erc::ERC721Events;
use evm_coder::ToLog;
-use frame_support::{BoundedVec, ensure, fail, transactional};
+use frame_support::{BoundedVec, ensure, fail, transactional, storage::with_transaction};
use up_data_structs::{
AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
mapping::TokenAddressMapping, NestingRule, budget::Budget, Property, PropertyPermission,
@@ -32,7 +32,7 @@
use pallet_structure::Pallet as PalletStructure;
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
use sp_core::H160;
-use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
+use sp_runtime::{ArithmeticError, DispatchError, DispatchResult, TransactionOutcome};
use sp_std::{vec::Vec, vec};
use core::ops::Deref;
use sp_std::collections::btree_map::BTreeMap;
@@ -600,28 +600,37 @@
// =========
+ with_transaction(|| {
+ for (i, data) in data.iter().enumerate() {
+ let token = first_token + i as u32 + 1;
+
+ <TokenData<T>>::insert(
+ (collection.id, token),
+ ItemData {
+ const_data: data.const_data.clone(),
+ owner: data.owner.clone(),
+ },
+ );
+
+ if let Err(e) = Self::set_token_properties(
+ collection,
+ sender,
+ TokenId(token),
+ data.properties.clone().into_inner(),
+ ) {
+ return TransactionOutcome::Rollback(Err(e));
+ }
+ }
+ TransactionOutcome::Commit(Ok(()))
+ })?;
+
<TokensMinted<T>>::insert(collection.id, tokens_minted);
for (account, balance) in balances {
<AccountBalance<T>>::insert((collection.id, account), balance);
}
for (i, data) in data.into_iter().enumerate() {
let token = first_token + i as u32 + 1;
-
- <TokenData<T>>::insert(
- (collection.id, token),
- ItemData {
- const_data: data.const_data,
- owner: data.owner.clone(),
- },
- );
<Owned<T>>::insert((collection.id, &data.owner, token), true);
-
- Self::set_token_properties(
- collection,
- sender,
- TokenId(token),
- data.properties.into_inner(),
- )?;
<PalletEvm<T>>::deposit_log(
ERC721Events::Transfer {
pallets/nonfungible/src/weights.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -151,10 +151,35 @@
// Storage: Nonfungible AccountBalance (r:1 w:1)
// Storage: Nonfungible Owned (r:0 w:1)
fn burn_from() -> Weight {
- (27_580_000 as Weight)
- .saturating_add(T::DbWeight::get().reads(4 as Weight))
- .saturating_add(T::DbWeight::get().writes(5 as Weight))
}
+ // Storage: Common CollectionPropertyPermissions (r:1 w:1)
+ fn set_property_permissions(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 3_432_000
+ .saturating_add((126_888_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionPropertyPermissions (r:1 w:0)
+ // Storage: Nonfungible TokenData (r:1 w:0)
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn set_token_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 158_583_000
+ .saturating_add((4_707_700_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(3 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionPropertyPermissions (r:1 w:0)
+ // Storage: Nonfungible TokenData (r:1 w:0)
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn delete_token_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 169_018_000
+ .saturating_add((4_783_967_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(3 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
}
// For backwards compatibility and tests
@@ -260,8 +285,33 @@
// Storage: Nonfungible AccountBalance (r:1 w:1)
// Storage: Nonfungible Owned (r:0 w:1)
fn burn_from() -> Weight {
- (27_580_000 as Weight)
- .saturating_add(RocksDbWeight::get().reads(4 as Weight))
- .saturating_add(RocksDbWeight::get().writes(5 as Weight))
}
+ // Storage: Common CollectionPropertyPermissions (r:1 w:1)
+ fn set_property_permissions(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 3_432_000
+ .saturating_add((126_888_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionPropertyPermissions (r:1 w:0)
+ // Storage: Nonfungible TokenData (r:1 w:0)
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn set_token_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 158_583_000
+ .saturating_add((4_707_700_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(3 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ // Storage: Common CollectionPropertyPermissions (r:1 w:0)
+ // Storage: Nonfungible TokenData (r:1 w:0)
+ // Storage: Nonfungible TokenProperties (r:1 w:1)
+ fn delete_token_properties(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 169_018_000
+ .saturating_add((4_783_967_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(3 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
}
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -18,7 +18,7 @@
use crate::{Pallet, Config, RefungibleHandle};
use sp_std::prelude::*;
-use pallet_common::benchmarking::{create_collection_raw, create_data, create_var_data};
+use pallet_common::benchmarking::{create_collection_raw, create_data};
use frame_benchmarking::{benchmarks, account};
use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH, CUSTOM_DATA_LIMIT, budget::Unlimited};
use pallet_common::bench_init;
@@ -46,7 +46,7 @@
users: impl IntoIterator<Item = (T::CrossAccountId, u128)>,
) -> Result<TokenId, DispatchError> {
let data: CreateRefungibleExData<T::CrossAccountId> = create_max_item_data(users);
- <Pallet<T>>::create_item(&collection, sender, data)?;
+ <Pallet<T>>::create_item(&collection, sender, data, &Unlimited)?;
Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
}
@@ -73,7 +73,7 @@
sender: cross_from_sub(owner); to: cross_sub;
};
let data = (0..b).map(|_| create_max_item_data([(to.clone(), 200)])).collect();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
create_multiple_items_ex_multiple_items {
let b in 0..MAX_ITEMS_PER_BATCH;
@@ -85,7 +85,7 @@
bench_init!(to: cross_sub(t););
create_max_item_data([(to, 200)])
}).collect();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
create_multiple_items_ex_multiple_owners {
let b in 0..MAX_ITEMS_PER_BATCH;
@@ -97,7 +97,7 @@
bench_init!(to: cross_sub(u););
(to, 200)
}))].try_into().unwrap();
- }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data, &Unlimited)?}
// Other user left, token data is kept
burn_item_partial {
@@ -122,7 +122,7 @@
sender: cross_from_sub(owner); receiver: cross_sub;
};
let item = create_max_item(&collection, &sender, [(sender.clone(), 200), (receiver.clone(), 200)])?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100)?}
+ }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100, &Unlimited)?}
// Target account is created
transfer_creating {
bench_init!{
@@ -130,7 +130,7 @@
sender: cross_from_sub(owner); receiver: cross_sub;
};
let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100)?}
+ }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 100, &Unlimited)?}
// Source account is destroyed
transfer_removing {
bench_init!{
@@ -138,7 +138,7 @@
sender: cross_from_sub(owner); receiver: cross_sub;
};
let item = create_max_item(&collection, &sender, [(sender.clone(), 200), (receiver.clone(), 200)])?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200)?}
+ }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200, &Unlimited)?}
// Source account destroyed, target created
transfer_creating_removing {
bench_init!{
@@ -146,7 +146,7 @@
sender: cross_from_sub(owner); receiver: cross_sub;
};
let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
- }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200)?}
+ }: {<Pallet<T>>::transfer(&collection, &sender, &receiver, item, 200, &Unlimited)?}
approve {
bench_init!{
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -20,7 +20,7 @@
use frame_support::{dispatch::DispatchResultWithPostInfo, fail, weights::Weight};
use up_data_structs::{
CollectionId, TokenId, CreateItemExData, CreateRefungibleExData, budget::Budget, Property,
- PropertyKey, PropertyValue, PropertyKeyPermission,
+ PropertyKey, PropertyValue, PropertyKeyPermission, CreateItemData,
};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::DispatchError;
@@ -46,8 +46,8 @@
<SelfWeightOf<T>>::create_item()
}
- fn create_multiple_items(amount: u32) -> Weight {
- <SelfWeightOf<T>>::create_multiple_items(amount)
+ fn create_multiple_items(data: &[CreateItemData]) -> Weight {
+ <SelfWeightOf<T>>::create_multiple_items(data.len() as u32)
}
fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {
@@ -66,12 +66,14 @@
max_weight_of!(burn_item_partial(), burn_item_fully())
}
- fn set_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::set_collection_properties(amount)
+ fn set_collection_properties(_amount: u32) -> Weight {
+ // Error
+ 0
}
- fn delete_collection_properties(amount: u32) -> Weight {
- <SelfWeightOf<T>>::delete_collection_properties(amount)
+ fn delete_collection_properties(_amount: u32) -> Weight {
+ // Error
+ 0
}
fn set_token_properties(amount: u32) -> Weight {
@@ -156,15 +158,15 @@
data: Vec<up_data_structs::CreateItemData>,
nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
+ let weight = <CommonWeights<T>>::create_multiple_items(&data);
let data = data
.into_iter()
.map(|d| map_create_data::<T>(d, &to))
.collect::<Result<Vec<_>, DispatchError>>()?;
- let amount = data.len();
with_weight(
<Pallet<T>>::create_multiple_items(self, &sender, data, nesting_budget),
- <CommonWeights<T>>::create_multiple_items(amount as u32),
+ weight,
)
}
pallets/refungible/src/weights.rsdiffbeforeafterboth--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -38,8 +38,6 @@
fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight;
fn burn_item_partial() -> Weight;
fn burn_item_fully() -> Weight;
- fn set_collection_properties(amount: u32) -> Weight;
- fn delete_collection_properties(amount: u32) -> Weight;
fn set_token_properties(amount: u32) -> Weight;
fn delete_token_properties(amount: u32) -> Weight;
fn set_property_permissions(amount: u32) -> Weight;
@@ -132,16 +130,6 @@
(32_489_000 as Weight)
.saturating_add(T::DbWeight::get().reads(4 as Weight))
.saturating_add(T::DbWeight::get().writes(6 as Weight))
- }
-
- fn set_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
}
fn set_token_properties(_amount: u32) -> Weight {
@@ -320,16 +308,6 @@
(32_489_000 as Weight)
.saturating_add(RocksDbWeight::get().reads(4 as Weight))
.saturating_add(RocksDbWeight::get().writes(6 as Weight))
- }
-
- fn set_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
- }
-
- fn delete_collection_properties(_amount: u32) -> Weight {
- // Error
- 0
}
fn set_token_properties(_amount: u32) -> Weight {
pallets/structure/Cargo.tomldiffbeforeafterboth--- a/pallets/structure/Cargo.toml
+++ b/pallets/structure/Cargo.toml
@@ -16,6 +16,7 @@
"derive",
] }
up-data-structs = { path = "../../primitives/data-structs", default-features = false }
+pallet-evm = { default-features = false, git = "https://github.com/uniquenetwork/frontier", branch = "unique-polkadot-v0.9.21-logs" }
[features]
default = ["std"]
@@ -28,5 +29,6 @@
"scale-info/std",
"parity-scale-codec/std",
"up-data-structs/std",
+ "pallet-evm/std",
]
runtime-benchmarks = ['frame-benchmarking', 'pallet-common/runtime-benchmarks']
pallets/structure/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/structure/src/benchmarking.rs
+++ b/pallets/structure/src/benchmarking.rs
@@ -2,8 +2,10 @@
use frame_benchmarking::{benchmarks, account};
use frame_support::traits::{Currency, Get};
-use up_data_structs::{CreateCollectionData, CollectionMode, CreateItemData, CreateNftData};
-use pallet_common::CrossAccountId;
+use up_data_structs::{
+ CreateCollectionData, CollectionMode, CreateItemData, CreateNftData, budget::Unlimited,
+};
+use pallet_evm::account::CrossAccountId;
const SEED: u32 = 1;
@@ -20,9 +22,9 @@
let dispatch = T::CollectionDispatch::dispatch(CollectionHandle::try_get(CollectionId(1))?);
let dispatch = dispatch.as_dyn();
- dispatch.create_item(caller_cross.clone(), caller_cross.clone(), CreateItemData::NFT(CreateNftData::default()))?;
+ dispatch.create_item(caller_cross.clone(), caller_cross.clone(), CreateItemData::NFT(CreateNftData::default()), &Unlimited)?;
}: {
let parent = <Pallet<T>>::find_parent(CollectionId(1), TokenId(1))?;
- assert!(matches!(parent, Parent::Normal(_)))
+ assert!(matches!(parent, Parent::User(_)))
}
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -687,7 +687,7 @@
/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].
///
/// * owner: Address, initial owner of the NFT.
- #[weight = T::CommonWeightInfo::create_multiple_items(items_data.len() as u32)]
+ #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]
#[transactional]
pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {
ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);
primitives/data-structs/Cargo.tomldiffbeforeafterboth--- a/primitives/data-structs/Cargo.toml
+++ b/primitives/data-structs/Cargo.toml
@@ -42,3 +42,4 @@
]
serde1 = ["serde"]
limit-testing = []
+runtime-benchmarks = []
\ No newline at end of file
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -861,7 +861,9 @@
) -> Result<(), PropertiesError> {
let value_len = value.len();
- if self.consumed_space as usize + value_len > self.space_limit as usize {
+ if self.consumed_space as usize + value_len > self.space_limit as usize
+ && !cfg!(feature = "runtime-benchmarks")
+ {
return Err(PropertiesError::NoSpaceForProperty);
}
runtime/common/src/eth_sponsoring.rsdiffbeforeafterboth--- a/runtime/common/src/eth_sponsoring.rs
+++ b/runtime/common/src/eth_sponsoring.rs
@@ -50,16 +50,20 @@
CollectionMode::NFT => {
let call = <UniqueNFTCall<T>>::parse(method_id, &mut reader).ok()??;
match call {
- UniqueNFTCall::TokenProperties(
- TokenPropertiesCall::SetProperty { token_id, key, value, .. },
- ) => {
+ UniqueNFTCall::TokenProperties(TokenPropertiesCall::SetProperty {
+ token_id,
+ key,
+ value,
+ ..
+ }) => {
let token_id: TokenId = token_id.try_into().ok()?;
withdraw_set_token_property::<T>(
&collection,
&who,
&token_id,
key.len() + value.len(),
- ).map(|()| sponsor)
+ )
+ .map(|()| sponsor)
}
UniqueNFTCall::ERC721UniqueExtensions(
ERC721UniqueExtensionsCall::Transfer { token_id, .. },
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -776,6 +776,7 @@
let mut list = Vec::<BenchmarkList>::new();
list_benchmark!(list, extra, pallet_evm_migration, EvmMigration);
+ list_benchmark!(list, extra, pallet_common, Common);
list_benchmark!(list, extra, pallet_unique, Unique);
list_benchmark!(list, extra, pallet_structure, Structure);
list_benchmark!(list, extra, pallet_inflation, Inflation);
@@ -814,6 +815,7 @@
let params = (&config, &allowlist);
add_benchmark!(params, batches, pallet_evm_migration, EvmMigration);
+ add_benchmark!(params, batches, pallet_common, Common);
add_benchmark!(params, batches, pallet_unique, Unique);
add_benchmark!(params, batches, pallet_structure, Structure);
add_benchmark!(params, batches, pallet_inflation, Inflation);
runtime/common/src/sponsoring.rsdiffbeforeafterboth--- a/runtime/common/src/sponsoring.rs
+++ b/runtime/common/src/sponsoring.rs
@@ -29,8 +29,8 @@
use pallet_evm::account::CrossAccountId;
use pallet_unique::{
Call as UniqueCall, Config as UniqueConfig, FungibleApproveBasket, RefungibleApproveBasket,
- NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket,
- FungibleTransferBasket, NftTransferBasket, TokenPropertyBasket,
+ NftApproveBasket, CreateItemBasket, ReFungibleTransferBasket, FungibleTransferBasket,
+ NftTransferBasket, TokenPropertyBasket,
};
use pallet_fungible::Config as FungibleConfig;
use pallet_nonfungible::Config as NonfungibleConfig;
@@ -247,7 +247,7 @@
&T::CrossAccountId::from_sub(who.clone()),
&token_id,
// No overflow may happen, as data larger than usize can't reach here
- properties.iter().map(|p| p.key.len() + p.value.len()).sum()
+ properties.iter().map(|p| p.key.len() + p.value.len()).sum(),
)
.map(|()| sponsor)
}
runtime/common/src/weights.rsdiffbeforeafterboth--- a/runtime/common/src/weights.rs
+++ b/runtime/common/src/weights.rs
@@ -21,7 +21,7 @@
use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights};
use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights};
use pallet_refungible::{Config as RefungibleConfig, common::CommonWeights as RefungibleWeights};
-use up_data_structs::CreateItemExData;
+use up_data_structs::{CreateItemExData, CreateItemData};
macro_rules! max_weight_of {
($method:ident ( $($args:tt)* )) => {
@@ -42,8 +42,8 @@
dispatch_weight::<T>() + max_weight_of!(create_item())
}
- fn create_multiple_items(amount: u32) -> Weight {
- dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
+ fn create_multiple_items(data: &[CreateItemData]) -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(create_multiple_items(data))
}
fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -875,6 +875,7 @@
}
impl pallet_common::Config for Runtime {
+ type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;
type Event = Event;
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
runtime/tests/src/lib.rsdiffbeforeafterboth--- a/runtime/tests/src/lib.rs
+++ b/runtime/tests/src/lib.rs
@@ -212,6 +212,7 @@
}
impl pallet_common::Config for Test {
+ type WeightInfo = ();
type Event = ();
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
runtime/unique/Cargo.tomldiffbeforeafterboth--- a/runtime/unique/Cargo.toml
+++ b/runtime/unique/Cargo.toml
@@ -39,6 +39,7 @@
'pallet-xcm/runtime-benchmarks',
'sp-runtime/runtime-benchmarks',
'xcm-builder/runtime-benchmarks',
+ 'up-data-structs/runtime-benchmarks',
]
try-runtime = [
'frame-try-runtime',
runtime/unique/src/lib.rsdiffbeforeafterboth--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -66,7 +66,12 @@
WeightToFeePolynomial, WeightToFeeCoefficient, WeightToFeeCoefficients, ConstantMultiplier,
},
};
-use unique_runtime_common::dispatch::{CollectionDispatchT, CollectionDispatch};
+use unique_runtime_common::{
+ dispatch::{CollectionDispatchT, CollectionDispatch},
+ weights::CommonWeights,
+ sponsoring::UniqueSponsorshipHandler,
+ eth_sponsoring::UniqueEthSponsorshipHandler,
+};
use up_data_structs::*;
// use pallet_contracts::weights::WeightInfo;
// #[cfg(any(feature = "std", test))]
@@ -846,6 +851,7 @@
}
impl pallet_common::Config for Runtime {
+ type WeightInfo = pallet_common::weights::SubstrateWeight<Self>;
type Event = Event;
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
@@ -881,6 +887,7 @@
impl pallet_unique::Config for Runtime {
type Event = Event;
type WeightInfo = pallet_unique::weights::SubstrateWeight<Self>;
+ type CommonWeightInfo = CommonWeights<Self>;
}
parameter_types! {
@@ -902,11 +909,11 @@
// }
type EvmSponsorshipHandler = (
- pallet_unique::UniqueEthSponsorshipHandler<Runtime>,
+ UniqueEthSponsorshipHandler<Runtime>,
pallet_evm_contract_helpers::HelpersContractSponsoring<Runtime>,
);
type SponsorshipHandler = (
- pallet_unique::UniqueSponsorshipHandler<Runtime>,
+ UniqueSponsorshipHandler<Runtime>,
//pallet_contract_helpers::ContractSponsorshipHandler<Runtime>,
pallet_evm_transaction_payment::BridgeSponsorshipHandler<Runtime>,
);