difftreelog
feat allow more fields to be set on collection creation
in: master
7 files changed
pallets/common/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use core::ops::{Deref, DerefMut};4use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};5use sp_std::vec::Vec;6use account::CrossAccountId;7use frame_support::{8 dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},9 ensure, fail,10 traits::{Imbalance, Get, Currency},11};12use pallet_evm::GasWeightMapping;13use up_data_structs::{14 COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,15 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,16 COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight,17 WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,18 NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,19 REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,20 CreateCollectionData, SponsorshipState,21};22pub use pallet::*;23use sp_core::H160;24use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};25pub mod account;26#[cfg(feature = "runtime-benchmarks")]27pub mod benchmarking;28pub mod erc;29pub mod eth;3031#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]32pub struct CollectionHandle<T: Config> {33 pub id: CollectionId,34 collection: Collection<T::AccountId>,35 pub recorder: SubstrateRecorder<T>,36}37impl<T: Config> WithRecorder<T> for CollectionHandle<T> {38 fn recorder(&self) -> &SubstrateRecorder<T> {39 &self.recorder40 }41 fn into_recorder(self) -> SubstrateRecorder<T> {42 self.recorder43 }44}45impl<T: Config> CollectionHandle<T> {46 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {47 <CollectionById<T>>::get(id).map(|collection| Self {48 id,49 collection,50 recorder: SubstrateRecorder::new(eth::collection_id_to_address(id), gas_limit),51 })52 }53 pub fn new(id: CollectionId) -> Option<Self> {54 Self::new_with_gas_limit(id, u64::MAX)55 }56 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {57 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)58 }59 pub fn log(&self, log: impl evm_coder::ToLog) {60 self.recorder.log(log)61 }62 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {63 self.recorder64 .consume_gas(T::GasWeightMapping::weight_to_gas(65 <T as frame_system::Config>::DbWeight::get()66 .read67 .saturating_mul(reads),68 ))69 }70 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {71 self.recorder72 .consume_gas(T::GasWeightMapping::weight_to_gas(73 <T as frame_system::Config>::DbWeight::get()74 .write75 .saturating_mul(writes),76 ))77 }78 pub fn submit_logs(self) {79 self.recorder.submit_logs()80 }81 pub fn save(self) -> DispatchResult {82 self.recorder.submit_logs();83 <CollectionById<T>>::insert(self.id, self.collection);84 Ok(())85 }86}87impl<T: Config> Deref for CollectionHandle<T> {88 type Target = Collection<T::AccountId>;8990 fn deref(&self) -> &Self::Target {91 &self.collection92 }93}9495impl<T: Config> DerefMut for CollectionHandle<T> {96 fn deref_mut(&mut self) -> &mut Self::Target {97 &mut self.collection98 }99}100101impl<T: Config> CollectionHandle<T> {102 pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {103 ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);104 Ok(())105 }106 pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {107 *subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))108 }109 pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {110 ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);111 Ok(())112 }113 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {114 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)115 }116 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {117 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)118 }119 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {120 ensure!(121 <Allowlist<T>>::get((self.id, user)),122 <Error<T>>::AddressNotInAllowlist123 );124 Ok(())125 }126127 pub fn check_can_update_meta(128 &self,129 subject: &T::CrossAccountId,130 item_owner: &T::CrossAccountId,131 ) -> DispatchResult {132 match self.meta_update_permission {133 MetaUpdatePermission::ItemOwner => {134 ensure!(subject == item_owner, <Error<T>>::NoPermission);135 Ok(())136 }137 MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),138 MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),139 }140 }141}142143#[frame_support::pallet]144pub mod pallet {145 use super::*;146 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};147 use account::CrossAccountId;148 use frame_support::traits::Currency;149 use up_data_structs::TokenId;150 use scale_info::TypeInfo;151152 #[pallet::config]153 pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo {154 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;155156 type CrossAccountId: CrossAccountId<Self::AccountId>;157158 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;159 type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;160161 type Currency: Currency<Self::AccountId>;162 type CollectionCreationPrice: Get<163 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,164 >;165 type TreasuryAccountId: Get<Self::AccountId>;166 }167168 #[pallet::pallet]169 #[pallet::generate_store(pub(super) trait Store)]170 pub struct Pallet<T>(_);171172 #[pallet::extra_constants]173 impl<T: Config> Pallet<T> {174 pub fn collection_admins_limit() -> u32 {175 COLLECTION_ADMINS_LIMIT176 }177 }178179 #[pallet::event]180 #[pallet::generate_deposit(pub fn deposit_event)]181 pub enum Event<T: Config> {182 /// New collection was created183 ///184 /// # Arguments185 ///186 /// * collection_id: Globally unique identifier of newly created collection.187 ///188 /// * mode: [CollectionMode] converted into u8.189 ///190 /// * account_id: Collection owner.191 CollectionCreated(CollectionId, u8, T::AccountId),192193 /// New collection was destroyed194 ///195 /// # Arguments196 ///197 /// * collection_id: Globally unique identifier of collection.198 CollectionDestroyed(CollectionId),199200 /// New item was created.201 ///202 /// # Arguments203 ///204 /// * collection_id: Id of the collection where item was created.205 ///206 /// * item_id: Id of an item. Unique within the collection.207 ///208 /// * recipient: Owner of newly created item209 ///210 /// * amount: Always 1 for NFT211 ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),212213 /// Collection item was burned.214 ///215 /// # Arguments216 ///217 /// * collection_id.218 ///219 /// * item_id: Identifier of burned NFT.220 ///221 /// * owner: which user has destroyed its tokens222 ///223 /// * amount: Always 1 for NFT224 ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),225226 /// Item was transferred227 ///228 /// * collection_id: Id of collection to which item is belong229 ///230 /// * item_id: Id of an item231 ///232 /// * sender: Original owner of item233 ///234 /// * recipient: New owner of item235 ///236 /// * amount: Always 1 for NFT237 Transfer(238 CollectionId,239 TokenId,240 T::CrossAccountId,241 T::CrossAccountId,242 u128,243 ),244245 /// * collection_id246 ///247 /// * item_id248 ///249 /// * sender250 ///251 /// * spender252 ///253 /// * amount254 Approved(255 CollectionId,256 TokenId,257 T::CrossAccountId,258 T::CrossAccountId,259 u128,260 ),261 }262263 #[pallet::error]264 pub enum Error<T> {265 /// This collection does not exist.266 CollectionNotFound,267 /// Sender parameter and item owner must be equal.268 MustBeTokenOwner,269 /// No permission to perform action270 NoPermission,271 /// Collection is not in mint mode.272 PublicMintingNotAllowed,273 /// Address is not in allow list.274 AddressNotInAllowlist,275276 /// Collection name can not be longer than 63 char.277 CollectionNameLimitExceeded,278 /// Collection description can not be longer than 255 char.279 CollectionDescriptionLimitExceeded,280 /// Token prefix can not be longer than 15 char.281 CollectionTokenPrefixLimitExceeded,282 /// Total collections bound exceeded.283 TotalCollectionsLimitExceeded,284 /// variable_data exceeded data limit.285 TokenVariableDataLimitExceeded,286 /// Exceeded max admin count287 CollectionAdminCountExceeded,288 /// Collection limit bounds per collection exceeded289 CollectionLimitBoundsExceeded,290 /// Tried to enable permissions which are only permitted to be disabled291 OwnerPermissionsCantBeReverted,292293 /// Collection settings not allowing items transferring294 TransferNotAllowed,295 /// Account token limit exceeded per collection296 AccountTokenLimitExceeded,297 /// Collection token limit exceeded298 CollectionTokenLimitExceeded,299 /// Metadata flag frozen300 MetadataFlagFrozen,301302 /// Item not exists.303 TokenNotFound,304 /// Item balance not enough.305 TokenValueTooLow,306 /// Requested value more than approved.307 TokenValueNotEnough,308 /// Tried to approve more than owned309 CantApproveMoreThanOwned,310311 /// Can't transfer tokens to ethereum zero address312 AddressIsZero,313 /// Target collection doesn't supports this operation314 UnsupportedOperation,315 }316317 #[pallet::storage]318 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;319 #[pallet::storage]320 pub type DestroyedCollectionCount<T> =321 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;322323 /// Collection info324 #[pallet::storage]325 pub type CollectionById<T> = StorageMap<326 Hasher = Blake2_128Concat,327 Key = CollectionId,328 Value = Collection<<T as frame_system::Config>::AccountId>,329 QueryKind = OptionQuery,330 >;331332 #[pallet::storage]333 pub type AdminAmount<T> = StorageMap<334 Hasher = Blake2_128Concat,335 Key = CollectionId,336 Value = u32,337 QueryKind = ValueQuery,338 >;339340 /// List of collection admins341 #[pallet::storage]342 pub type IsAdmin<T: Config> = StorageNMap<343 Key = (344 Key<Blake2_128Concat, CollectionId>,345 Key<Blake2_128Concat, T::CrossAccountId>,346 ),347 Value = bool,348 QueryKind = ValueQuery,349 >;350351 /// Allowlisted collection users352 #[pallet::storage]353 pub type Allowlist<T: Config> = StorageNMap<354 Key = (355 Key<Blake2_128Concat, CollectionId>,356 Key<Blake2_128Concat, T::CrossAccountId>,357 ),358 Value = bool,359 QueryKind = ValueQuery,360 >;361362 /// Not used by code, exists only to provide some types to metadata363 #[pallet::storage]364 pub type DummyStorageValue<T> =365 StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;366}367368impl<T: Config> Pallet<T> {369 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens370 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {371 ensure!(372 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,373 <Error<T>>::AddressIsZero374 );375 Ok(())376 }377 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {378 <IsAdmin<T>>::iter_prefix((collection,))379 .map(|(a, _)| a)380 .collect()381 }382 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {383 <Allowlist<T>>::iter_prefix((collection,))384 .map(|(a, _)| a)385 .collect()386 }387 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {388 <Allowlist<T>>::get((collection, user))389 }390 pub fn collection_stats() -> CollectionStats {391 let created = <CreatedCollectionCount<T>>::get();392 let destroyed = <DestroyedCollectionCount<T>>::get();393 CollectionStats {394 created: created.0,395 destroyed: destroyed.0,396 alive: created.0 - destroyed.0,397 }398 }399}400401impl<T: Config> Pallet<T> {402 pub fn init_collection(403 owner: T::AccountId,404 data: CreateCollectionData<T::AccountId>,405 ) -> Result<CollectionId, DispatchError> {406 {407 ensure!(408 data.name.len() <= MAX_COLLECTION_NAME_LENGTH,409 Error::<T>::CollectionNameLimitExceeded410 );411 ensure!(412 data.description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH,413 Error::<T>::CollectionDescriptionLimitExceeded414 );415 ensure!(416 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH,417 Error::<T>::CollectionTokenPrefixLimitExceeded418 );419 }420421 let created_count = <CreatedCollectionCount<T>>::get()422 .0423 .checked_add(1)424 .ok_or(ArithmeticError::Overflow)?;425 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;426 let id = CollectionId(created_count);427428 // bound Total number of collections429 ensure!(430 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,431 <Error<T>>::TotalCollectionsLimitExceeded432 );433434 // =========435436 let collection = Collection {437 owner: owner.clone(),438 name: data.name,439 mode: data.mode.clone(),440 mint_mode: false,441 access: data.access.unwrap_or_default(),442 description: data.description,443 token_prefix: data.token_prefix,444 offchain_schema: data.offchain_schema,445 schema_version: data.schema_version.unwrap_or_default(),446 sponsorship: data447 .pending_sponsor448 .map(SponsorshipState::Unconfirmed)449 .unwrap_or_default(),450 variable_on_chain_schema: data.variable_on_chain_schema,451 const_on_chain_schema: data.const_on_chain_schema,452 limits: data453 .limits454 .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits))455 .unwrap_or_else(|| Ok(CollectionLimits::default()))?,456 meta_update_permission: data.meta_update_permission.unwrap_or_default(),457 };458459 // Take a (non-refundable) deposit of collection creation460 {461 let mut imbalance =462 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();463 imbalance.subsume(464 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(465 &T::TreasuryAccountId::get(),466 T::CollectionCreationPrice::get(),467 ),468 );469 <T as Config>::Currency::settle(470 &owner,471 imbalance,472 WithdrawReasons::TRANSFER,473 ExistenceRequirement::KeepAlive,474 )475 .map_err(|_| Error::<T>::NoPermission)?;476 }477478 <CreatedCollectionCount<T>>::put(created_count);479 <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));480 <CollectionById<T>>::insert(id, collection);481 Ok(id)482 }483484 pub fn destroy_collection(485 collection: CollectionHandle<T>,486 sender: &T::CrossAccountId,487 ) -> DispatchResult {488 ensure!(489 collection.limits.owner_can_destroy(),490 <Error<T>>::NoPermission,491 );492 collection.check_is_owner(sender)?;493494 let destroyed_collections = <DestroyedCollectionCount<T>>::get()495 .0496 .checked_add(1)497 .ok_or(ArithmeticError::Overflow)?;498499 // =========500501 <DestroyedCollectionCount<T>>::put(destroyed_collections);502 <CollectionById<T>>::remove(collection.id);503 <AdminAmount<T>>::remove(collection.id);504 <IsAdmin<T>>::remove_prefix((collection.id,), None);505 <Allowlist<T>>::remove_prefix((collection.id,), None);506507 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));508 Ok(())509 }510511 pub fn toggle_allowlist(512 collection: &CollectionHandle<T>,513 sender: &T::CrossAccountId,514 user: &T::CrossAccountId,515 allowed: bool,516 ) -> DispatchResult {517 collection.check_is_owner_or_admin(sender)?;518519 // =========520521 if allowed {522 <Allowlist<T>>::insert((collection.id, user), true);523 } else {524 <Allowlist<T>>::remove((collection.id, user));525 }526527 Ok(())528 }529530 pub fn toggle_admin(531 collection: &CollectionHandle<T>,532 sender: &T::CrossAccountId,533 user: &T::CrossAccountId,534 admin: bool,535 ) -> DispatchResult {536 collection.check_is_owner_or_admin(sender)?;537538 let was_admin = <IsAdmin<T>>::get((collection.id, user));539 if was_admin == admin {540 return Ok(());541 }542 let amount = <AdminAmount<T>>::get(collection.id);543544 if admin {545 let amount = amount546 .checked_add(1)547 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;548 ensure!(549 amount <= Self::collection_admins_limit(),550 <Error<T>>::CollectionAdminCountExceeded,551 );552553 // =========554555 <AdminAmount<T>>::insert(collection.id, amount);556 <IsAdmin<T>>::insert((collection.id, user), true);557 } else {558 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));559 <IsAdmin<T>>::remove((collection.id, user));560 }561562 Ok(())563 }564565 pub fn clamp_limits(566 mode: CollectionMode,567 old_limit: &CollectionLimits,568 mut new_limit: CollectionLimits,569 ) -> Result<CollectionLimits, DispatchError> {570 macro_rules! limit_default {571 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{572 $(573 if let Some($new) = $new.$field {574 let $old = $old.$field($($arg)?);575 let _ = $new;576 let _ = $old;577 $check578 } else {579 $new.$field = $old.$field580 }581 )*582 }};583 }584585 limit_default!(old_limit, new_limit,586 account_token_ownership_limit => ensure!(587 new_limit <= MAX_TOKEN_OWNERSHIP,588 <Error<T>>::CollectionLimitBoundsExceeded,589 ),590 sponsor_transfer_timeout(match mode {591 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,592 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,593 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,594 }) => ensure!(595 new_limit <= MAX_SPONSOR_TIMEOUT,596 <Error<T>>::CollectionLimitBoundsExceeded,597 ),598 sponsored_data_size => ensure!(599 new_limit <= CUSTOM_DATA_LIMIT,600 <Error<T>>::CollectionLimitBoundsExceeded,601 ),602 token_limit => ensure!(603 old_limit >= new_limit && new_limit > 0,604 <Error<T>>::CollectionTokenLimitExceeded605 ),606 owner_can_transfer => ensure!(607 old_limit || !new_limit,608 <Error<T>>::OwnerPermissionsCantBeReverted,609 ),610 owner_can_destroy => ensure!(611 old_limit || !new_limit,612 <Error<T>>::OwnerPermissionsCantBeReverted,613 ),614 sponsored_data_rate_limit => {},615 transfers_enabled => {},616 );617 Ok(new_limit)618 }619}620621#[macro_export]622macro_rules! unsupported {623 () => {624 Err(<Error<T>>::UnsupportedOperation.into())625 };626}627628/// Worst cases629pub trait CommonWeightInfo {630 fn create_item() -> Weight;631 fn create_multiple_items(amount: u32) -> Weight;632 fn burn_item() -> Weight;633 fn transfer() -> Weight;634 fn approve() -> Weight;635 fn transfer_from() -> Weight;636 fn burn_from() -> Weight;637 fn set_variable_metadata(bytes: u32) -> Weight;638}639640pub trait CommonCollectionOperations<T: Config> {641 fn create_item(642 &self,643 sender: T::CrossAccountId,644 to: T::CrossAccountId,645 data: CreateItemData,646 ) -> DispatchResultWithPostInfo;647 fn create_multiple_items(648 &self,649 sender: T::CrossAccountId,650 to: T::CrossAccountId,651 data: Vec<CreateItemData>,652 ) -> DispatchResultWithPostInfo;653 fn burn_item(654 &self,655 sender: T::CrossAccountId,656 token: TokenId,657 amount: u128,658 ) -> DispatchResultWithPostInfo;659660 fn transfer(661 &self,662 sender: T::CrossAccountId,663 to: T::CrossAccountId,664 token: TokenId,665 amount: u128,666 ) -> DispatchResultWithPostInfo;667 fn approve(668 &self,669 sender: T::CrossAccountId,670 spender: T::CrossAccountId,671 token: TokenId,672 amount: u128,673 ) -> DispatchResultWithPostInfo;674 fn transfer_from(675 &self,676 sender: T::CrossAccountId,677 from: T::CrossAccountId,678 to: T::CrossAccountId,679 token: TokenId,680 amount: u128,681 ) -> DispatchResultWithPostInfo;682 fn burn_from(683 &self,684 sender: T::CrossAccountId,685 from: T::CrossAccountId,686 token: TokenId,687 amount: u128,688 ) -> DispatchResultWithPostInfo;689690 fn set_variable_metadata(691 &self,692 sender: T::CrossAccountId,693 token: TokenId,694 data: Vec<u8>,695 ) -> DispatchResultWithPostInfo;696697 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;698 fn token_exists(&self, token: TokenId) -> bool;699 fn last_token_id(&self) -> TokenId;700701 fn token_owner(&self, token: TokenId) -> T::CrossAccountId;702 fn const_metadata(&self, token: TokenId) -> Vec<u8>;703 fn variable_metadata(&self, token: TokenId) -> Vec<u8>;704705 /// How many tokens collection contains (Applicable to nonfungible/refungible)706 fn collection_tokens(&self) -> u32;707 /// Amount of different tokens account has (Applicable to nonfungible/refungible)708 fn account_balance(&self, account: T::CrossAccountId) -> u32;709 /// Amount of specific token account have (Applicable to fungible/refungible)710 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;711 fn allowance(712 &self,713 sender: T::CrossAccountId,714 spender: T::CrossAccountId,715 token: TokenId,716 ) -> u128;717}718719// Flexible enough for implementing CommonCollectionOperations720pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {721 let post_info = PostDispatchInfo {722 actual_weight: Some(weight),723 pays_fee: Pays::Yes,724 };725 match res {726 Ok(()) => Ok(post_info),727 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),728 }729}pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -2,7 +2,7 @@
use core::ops::Deref;
use frame_support::{ensure};
-use up_data_structs::{AccessMode, Collection, CollectionId, TokenId};
+use up_data_structs::{AccessMode, Collection, CollectionId, TokenId, CreateCollectionData};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
};
@@ -100,8 +100,11 @@
}
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: FungibleHandle<T>,
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -4,6 +4,7 @@
use frame_support::{BoundedVec, ensure};
use up_data_structs::{
AccessMode, CUSTOM_DATA_LIMIT, Collection, CollectionId, CustomDataLimit, TokenId,
+ CreateCollectionData,
};
use pallet_common::{
Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
@@ -142,8 +143,11 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: NonfungibleHandle<T>,
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -3,7 +3,7 @@
use frame_support::{ensure, BoundedVec};
use up_data_structs::{
AccessMode, CUSTOM_DATA_LIMIT, Collection, CollectionId, CustomDataLimit,
- MAX_REFUNGIBLE_PIECES, TokenId,
+ MAX_REFUNGIBLE_PIECES, TokenId, CreateCollectionData,
};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -156,8 +156,11 @@
// unchecked calls skips any permission checks
impl<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(data)
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
+ <PalletCommon<T>>::init_collection(owner, data)
}
pub fn destroy_collection(
collection: RefungibleHandle<T>,
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -35,11 +35,10 @@
use frame_system::{self as system, ensure_signed};
use sp_runtime::{sp_std::prelude::Vec};
use up_data_structs::{
- MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,
- VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,
- FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits,
- CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
+ MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,
+ OFFCHAIN_SCHEMA_LIMIT, AccessMode, CreateItemData, CollectionLimits, CollectionId,
+ CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
+ CreateCollectionData,
};
use pallet_common::{
account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -81,10 +80,6 @@
ConfirmUnsetSponsorFail,
/// Length of items properties must be greater than 0.
EmptyArgument,
- /// Collection limit bounds per collection exceeded
- CollectionLimitBoundsExceeded,
- /// Tried to enable permissions which are only permitted to be disabled
- OwnerPermissionsCantBeReverted,
}
}
@@ -318,42 +313,38 @@
// returns collection ID
#[weight = <SelfWeightOf<T>>::create_collection()]
#[transactional]
+ #[deprecated]
pub fn create_collection(origin,
collection_name: Vec<u16>,
collection_description: Vec<u16>,
token_prefix: Vec<u8>,
mode: CollectionMode) -> DispatchResult {
-
- // Anyone can create a collection
- let who = ensure_signed(origin)?;
-
- // Create new collection
- let new_collection = Collection {
- owner: who,
+ Self::create_collection_ex(origin, CreateCollectionData {
name: collection_name,
- mode: mode.clone(),
- mint_mode: false,
- access: AccessMode::Normal,
description: collection_description,
token_prefix,
- offchain_schema: Vec::new(),
- schema_version: SchemaVersion::ImageURL,
- sponsorship: SponsorshipState::Disabled,
- variable_on_chain_schema: Vec::new(),
- const_on_chain_schema: Vec::new(),
- limits: Default::default(),
- meta_update_permission: Default::default(),
- };
+ mode,
+ ..Default::default()
+ })
+ }
+
+ /// This method creates a collection
+ ///
+ /// Prefer it to deprecated [`created_collection`] method
+ #[weight = <SelfWeightOf<T>>::create_collection()]
+ #[transactional]
+ pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {
+ let owner = ensure_signed(origin)?;
- let _id = match mode {
- CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},
+ let _id = match data.mode {
+ CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},
CollectionMode::Fungible(decimal_points) => {
// check params
ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);
- <PalletFungible<T>>::init_collection(new_collection)?
+ <PalletFungible<T>>::init_collection(owner, data)?
}
CollectionMode::ReFungible => {
- <PalletRefungible<T>>::init_collection(new_collection)?
+ <PalletRefungible<T>>::init_collection(owner, data)?
}
};
@@ -1099,61 +1090,12 @@
collection_id: CollectionId,
new_limit: CollectionLimits,
) -> DispatchResult {
- let mut new_limit = new_limit;
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
let old_limit = &target_collection.limits;
- macro_rules! limit_default {
- ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{
- $(
- if let Some($new) = $new.$field {
- let $old = $old.$field($($arg)?);
- let _ = $new;
- let _ = $old;
- $check
- } else {
- $new.$field = $old.$field
- }
- )*
- }};
- }
-
- limit_default!(old_limit, new_limit,
- account_token_ownership_limit => ensure!(
- new_limit <= MAX_TOKEN_OWNERSHIP,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsor_transfer_timeout(match target_collection.mode {
- CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
- }) => ensure!(
- new_limit <= MAX_SPONSOR_TIMEOUT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- sponsored_data_size => ensure!(
- new_limit <= CUSTOM_DATA_LIMIT,
- <Error<T>>::CollectionLimitBoundsExceeded,
- ),
- token_limit => ensure!(
- old_limit >= new_limit && new_limit > 0,
- <CommonError<T>>::CollectionTokenLimitExceeded
- ),
- owner_can_transfer => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- owner_can_destroy => ensure!(
- old_limit || !new_limit,
- <Error<T>>::OwnerPermissionsCantBeReverted,
- ),
- sponsored_data_rate_limit => {},
- transfers_enabled => {},
- );
-
- target_collection.limits = new_limit;
+ target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(
collection_id
pallets/unique/src/tests.rsdiffbeforeafterboth--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -5,7 +5,7 @@
use up_data_structs::{
COLLECTION_NUMBER_LIMIT, CollectionId, CreateItemData, CreateFungibleData, CreateNftData,
CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission,
- TokenId,
+ TokenId, MAX_TOKEN_OWNERSHIP,
};
use frame_support::{assert_noop, assert_ok};
use sp_std::convert::TryInto;
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -230,6 +230,25 @@
pub meta_update_permission: MetaUpdatePermission,
}
+#[derive(Encode, Decode, Clone, PartialEq, TypeInfo, Debug, Derivative)]
+#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
+#[derivative(Default)]
+pub struct CreateCollectionData<AccountId> {
+ #[derivative(Default(value = "CollectionMode::NFT"))]
+ pub mode: CollectionMode,
+ pub access: Option<AccessMode>,
+ pub name: Vec<u16>,
+ pub description: Vec<u16>,
+ pub token_prefix: Vec<u8>,
+ pub offchain_schema: Vec<u8>,
+ pub schema_version: Option<SchemaVersion>,
+ pub pending_sponsor: Option<AccountId>,
+ pub limits: Option<CollectionLimits>,
+ pub variable_on_chain_schema: Vec<u8>,
+ pub const_on_chain_schema: Vec<u8>,
+ pub meta_update_permission: Option<MetaUpdatePermission>,
+}
+
#[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub struct NftItemType<AccountId> {