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,18};19pub use pallet::*;20use sp_core::H160;21use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};22pub mod account;23#[cfg(feature = "runtime-benchmarks")]24pub mod benchmarking;25pub mod erc;26pub mod eth;2728#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]29pub struct CollectionHandle<T: Config> {30 pub id: CollectionId,31 collection: Collection<T::AccountId>,32 pub recorder: SubstrateRecorder<T>,33}34impl<T: Config> WithRecorder<T> for CollectionHandle<T> {35 fn recorder(&self) -> &SubstrateRecorder<T> {36 &self.recorder37 }38 fn into_recorder(self) -> SubstrateRecorder<T> {39 self.recorder40 }41}42impl<T: Config> CollectionHandle<T> {43 pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {44 <CollectionById<T>>::get(id).map(|collection| Self {45 id,46 collection,47 recorder: SubstrateRecorder::new(eth::collection_id_to_address(id), gas_limit),48 })49 }50 pub fn new(id: CollectionId) -> Option<Self> {51 Self::new_with_gas_limit(id, u64::MAX)52 }53 pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {54 Ok(Self::new(id).ok_or(<Error<T>>::CollectionNotFound)?)55 }56 pub fn log(&self, log: impl evm_coder::ToLog) {57 self.recorder.log(log)58 }59 pub fn consume_store_reads(&self, reads: u64) -> evm_coder::execution::Result<()> {60 self.recorder61 .consume_gas(T::GasWeightMapping::weight_to_gas(62 <T as frame_system::Config>::DbWeight::get()63 .read64 .saturating_mul(reads),65 ))66 }67 pub fn consume_store_writes(&self, writes: u64) -> evm_coder::execution::Result<()> {68 self.recorder69 .consume_gas(T::GasWeightMapping::weight_to_gas(70 <T as frame_system::Config>::DbWeight::get()71 .write72 .saturating_mul(writes),73 ))74 }75 pub fn submit_logs(self) {76 self.recorder.submit_logs()77 }78 pub fn save(self) -> DispatchResult {79 self.recorder.submit_logs();80 <CollectionById<T>>::insert(self.id, self.collection);81 Ok(())82 }83}84impl<T: Config> Deref for CollectionHandle<T> {85 type Target = Collection<T::AccountId>;8687 fn deref(&self) -> &Self::Target {88 &self.collection89 }90}9192impl<T: Config> DerefMut for CollectionHandle<T> {93 fn deref_mut(&mut self) -> &mut Self::Target {94 &mut self.collection95 }96}9798impl<T: Config> CollectionHandle<T> {99 pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {100 ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);101 Ok(())102 }103 pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> bool {104 *subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject))105 }106 pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {107 ensure!(self.is_owner_or_admin(subject), <Error<T>>::NoPermission);108 Ok(())109 }110 pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> bool {111 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)112 }113 pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> bool {114 self.limits.owner_can_transfer() && self.is_owner_or_admin(user)115 }116 pub fn check_allowlist(&self, user: &T::CrossAccountId) -> DispatchResult {117 ensure!(118 <Allowlist<T>>::get((self.id, user)),119 <Error<T>>::AddressNotInAllowlist120 );121 Ok(())122 }123124 pub fn check_can_update_meta(125 &self,126 subject: &T::CrossAccountId,127 item_owner: &T::CrossAccountId,128 ) -> DispatchResult {129 match self.meta_update_permission {130 MetaUpdatePermission::ItemOwner => {131 ensure!(subject == item_owner, <Error<T>>::NoPermission);132 Ok(())133 }134 MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),135 MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),136 }137 }138}139140#[frame_support::pallet]141pub mod pallet {142 use super::*;143 use frame_support::{Blake2_128Concat, pallet_prelude::*, storage::Key};144 use account::CrossAccountId;145 use frame_support::traits::Currency;146 use up_data_structs::TokenId;147 use scale_info::TypeInfo;148149 #[pallet::config]150 pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo {151 type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;152153 type CrossAccountId: CrossAccountId<Self::AccountId>;154155 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;156 type EvmBackwardsAddressMapping: up_evm_mapping::EvmBackwardsAddressMapping<Self::AccountId>;157158 type Currency: Currency<Self::AccountId>;159 type CollectionCreationPrice: Get<160 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,161 >;162 type TreasuryAccountId: Get<Self::AccountId>;163 }164165 #[pallet::pallet]166 #[pallet::generate_store(pub(super) trait Store)]167 pub struct Pallet<T>(_);168169 #[pallet::extra_constants]170 impl<T: Config> Pallet<T> {171 pub fn collection_admins_limit() -> u32 {172 COLLECTION_ADMINS_LIMIT173 }174 }175176 #[pallet::event]177 #[pallet::generate_deposit(pub fn deposit_event)]178 pub enum Event<T: Config> {179 /// New collection was created180 ///181 /// # Arguments182 ///183 /// * collection_id: Globally unique identifier of newly created collection.184 ///185 /// * mode: [CollectionMode] converted into u8.186 ///187 /// * account_id: Collection owner.188 CollectionCreated(CollectionId, u8, T::AccountId),189190 /// New collection was destroyed191 ///192 /// # Arguments193 ///194 /// * collection_id: Globally unique identifier of collection.195 CollectionDestroyed(CollectionId),196197 /// New item was created.198 ///199 /// # Arguments200 ///201 /// * collection_id: Id of the collection where item was created.202 ///203 /// * item_id: Id of an item. Unique within the collection.204 ///205 /// * recipient: Owner of newly created item206 ///207 /// * amount: Always 1 for NFT208 ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),209210 /// Collection item was burned.211 ///212 /// # Arguments213 ///214 /// * collection_id.215 ///216 /// * item_id: Identifier of burned NFT.217 ///218 /// * owner: which user has destroyed its tokens219 ///220 /// * amount: Always 1 for NFT221 ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),222223 /// Item was transferred224 ///225 /// * collection_id: Id of collection to which item is belong226 ///227 /// * item_id: Id of an item228 ///229 /// * sender: Original owner of item230 ///231 /// * recipient: New owner of item232 ///233 /// * amount: Always 1 for NFT234 Transfer(235 CollectionId,236 TokenId,237 T::CrossAccountId,238 T::CrossAccountId,239 u128,240 ),241242 /// * collection_id243 ///244 /// * item_id245 ///246 /// * sender247 ///248 /// * spender249 ///250 /// * amount251 Approved(252 CollectionId,253 TokenId,254 T::CrossAccountId,255 T::CrossAccountId,256 u128,257 ),258 }259260 #[pallet::error]261 pub enum Error<T> {262 /// This collection does not exist.263 CollectionNotFound,264 /// Sender parameter and item owner must be equal.265 MustBeTokenOwner,266 /// No permission to perform action267 NoPermission,268 /// Collection is not in mint mode.269 PublicMintingNotAllowed,270 /// Address is not in allow list.271 AddressNotInAllowlist,272273 /// Collection name can not be longer than 63 char.274 CollectionNameLimitExceeded,275 /// Collection description can not be longer than 255 char.276 CollectionDescriptionLimitExceeded,277 /// Token prefix can not be longer than 15 char.278 CollectionTokenPrefixLimitExceeded,279 /// Total collections bound exceeded.280 TotalCollectionsLimitExceeded,281 /// variable_data exceeded data limit.282 TokenVariableDataLimitExceeded,283 /// Exceeded max admin count284 CollectionAdminCountExceeded,285286 /// Collection settings not allowing items transferring287 TransferNotAllowed,288 /// Account token limit exceeded per collection289 AccountTokenLimitExceeded,290 /// Collection token limit exceeded291 CollectionTokenLimitExceeded,292 /// Metadata flag frozen293 MetadataFlagFrozen,294295 /// Item not exists.296 TokenNotFound,297 /// Item balance not enough.298 TokenValueTooLow,299 /// Requested value more than approved.300 TokenValueNotEnough,301 /// Tried to approve more than owned302 CantApproveMoreThanOwned,303304 /// Can't transfer tokens to ethereum zero address305 AddressIsZero,306 /// Target collection doesn't supports this operation307 UnsupportedOperation,308 }309310 #[pallet::storage]311 pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;312 #[pallet::storage]313 pub type DestroyedCollectionCount<T> =314 StorageValue<Value = CollectionId, QueryKind = ValueQuery>;315316 /// Collection info317 #[pallet::storage]318 pub type CollectionById<T> = StorageMap<319 Hasher = Blake2_128Concat,320 Key = CollectionId,321 Value = Collection<<T as frame_system::Config>::AccountId>,322 QueryKind = OptionQuery,323 >;324325 #[pallet::storage]326 pub type AdminAmount<T> = StorageMap<327 Hasher = Blake2_128Concat,328 Key = CollectionId,329 Value = u32,330 QueryKind = ValueQuery,331 >;332333 /// List of collection admins334 #[pallet::storage]335 pub type IsAdmin<T: Config> = StorageNMap<336 Key = (337 Key<Blake2_128Concat, CollectionId>,338 Key<Blake2_128Concat, T::CrossAccountId>,339 ),340 Value = bool,341 QueryKind = ValueQuery,342 >;343344 /// Allowlisted collection users345 #[pallet::storage]346 pub type Allowlist<T: Config> = StorageNMap<347 Key = (348 Key<Blake2_128Concat, CollectionId>,349 Key<Blake2_128Concat, T::CrossAccountId>,350 ),351 Value = bool,352 QueryKind = ValueQuery,353 >;354355 /// Not used by code, exists only to provide some types to metadata356 #[pallet::storage]357 pub type DummyStorageValue<T> =358 StorageValue<Value = (CollectionStats, CollectionId, TokenId), QueryKind = OptionQuery>;359}360361impl<T: Config> Pallet<T> {362 /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens363 pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {364 ensure!(365 &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,366 <Error<T>>::AddressIsZero367 );368 Ok(())369 }370 pub fn adminlist(collection: CollectionId) -> Vec<T::CrossAccountId> {371 <IsAdmin<T>>::iter_prefix((collection,))372 .map(|(a, _)| a)373 .collect()374 }375 pub fn allowlist(collection: CollectionId) -> Vec<T::CrossAccountId> {376 <Allowlist<T>>::iter_prefix((collection,))377 .map(|(a, _)| a)378 .collect()379 }380 pub fn allowed(collection: CollectionId, user: T::CrossAccountId) -> bool {381 <Allowlist<T>>::get((collection, user))382 }383 pub fn collection_stats() -> CollectionStats {384 let created = <CreatedCollectionCount<T>>::get();385 let destroyed = <DestroyedCollectionCount<T>>::get();386 CollectionStats {387 created: created.0,388 destroyed: destroyed.0,389 alive: created.0 - destroyed.0,390 }391 }392}393394impl<T: Config> Pallet<T> {395 pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {396 {397 ensure!(398 data.name.len() <= MAX_COLLECTION_NAME_LENGTH,399 Error::<T>::CollectionNameLimitExceeded400 );401 ensure!(402 data.description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH,403 Error::<T>::CollectionDescriptionLimitExceeded404 );405 ensure!(406 data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH,407 Error::<T>::CollectionTokenPrefixLimitExceeded408 );409 }410411 let created_count = <CreatedCollectionCount<T>>::get()412 .0413 .checked_add(1)414 .ok_or(ArithmeticError::Overflow)?;415 let destroyed_count = <DestroyedCollectionCount<T>>::get().0;416 let id = CollectionId(created_count);417418 // bound Total number of collections419 ensure!(420 created_count - destroyed_count <= COLLECTION_NUMBER_LIMIT,421 <Error<T>>::TotalCollectionsLimitExceeded422 );423424 // =========425426 // Take a (non-refundable) deposit of collection creation427 {428 let mut imbalance =429 <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();430 imbalance.subsume(431 <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(432 &T::TreasuryAccountId::get(),433 T::CollectionCreationPrice::get(),434 ),435 );436 <T as Config>::Currency::settle(437 &data.owner,438 imbalance,439 WithdrawReasons::TRANSFER,440 ExistenceRequirement::KeepAlive,441 )442 .map_err(|_| Error::<T>::NoPermission)?;443 }444445 <CreatedCollectionCount<T>>::put(created_count);446 <Pallet<T>>::deposit_event(Event::CollectionCreated(447 id,448 data.mode.id(),449 data.owner.clone(),450 ));451 <CollectionById<T>>::insert(id, data);452 Ok(id)453 }454455 pub fn destroy_collection(456 collection: CollectionHandle<T>,457 sender: &T::CrossAccountId,458 ) -> DispatchResult {459 ensure!(460 collection.limits.owner_can_destroy(),461 <Error<T>>::NoPermission,462 );463 collection.check_is_owner(sender)?;464465 let destroyed_collections = <DestroyedCollectionCount<T>>::get()466 .0467 .checked_add(1)468 .ok_or(ArithmeticError::Overflow)?;469470 // =========471472 <DestroyedCollectionCount<T>>::put(destroyed_collections);473 <CollectionById<T>>::remove(collection.id);474 <AdminAmount<T>>::remove(collection.id);475 <IsAdmin<T>>::remove_prefix((collection.id,), None);476 <Allowlist<T>>::remove_prefix((collection.id,), None);477478 <Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));479 Ok(())480 }481482 pub fn toggle_allowlist(483 collection: &CollectionHandle<T>,484 sender: &T::CrossAccountId,485 user: &T::CrossAccountId,486 allowed: bool,487 ) -> DispatchResult {488 collection.check_is_owner_or_admin(sender)?;489490 // =========491492 if allowed {493 <Allowlist<T>>::insert((collection.id, user), true);494 } else {495 <Allowlist<T>>::remove((collection.id, user));496 }497498 Ok(())499 }500501 pub fn toggle_admin(502 collection: &CollectionHandle<T>,503 sender: &T::CrossAccountId,504 user: &T::CrossAccountId,505 admin: bool,506 ) -> DispatchResult {507 collection.check_is_owner_or_admin(sender)?;508509 let was_admin = <IsAdmin<T>>::get((collection.id, user));510 if was_admin == admin {511 return Ok(());512 }513 let amount = <AdminAmount<T>>::get(collection.id);514515 if admin {516 let amount = amount517 .checked_add(1)518 .ok_or(<Error<T>>::CollectionAdminCountExceeded)?;519 ensure!(520 amount <= Self::collection_admins_limit(),521 <Error<T>>::CollectionAdminCountExceeded,522 );523524 // =========525526 <AdminAmount<T>>::insert(collection.id, amount);527 <IsAdmin<T>>::insert((collection.id, user), true);528 } else {529 <AdminAmount<T>>::insert(collection.id, amount.saturating_sub(1));530 <IsAdmin<T>>::remove((collection.id, user));531 }532533 Ok(())534 }535}536537#[macro_export]538macro_rules! unsupported {539 () => {540 Err(<Error<T>>::UnsupportedOperation.into())541 };542}543544/// Worst cases545pub trait CommonWeightInfo {546 fn create_item() -> Weight;547 fn create_multiple_items(amount: u32) -> Weight;548 fn burn_item() -> Weight;549 fn transfer() -> Weight;550 fn approve() -> Weight;551 fn transfer_from() -> Weight;552 fn burn_from() -> Weight;553 fn set_variable_metadata(bytes: u32) -> Weight;554}555556pub trait CommonCollectionOperations<T: Config> {557 fn create_item(558 &self,559 sender: T::CrossAccountId,560 to: T::CrossAccountId,561 data: CreateItemData,562 ) -> DispatchResultWithPostInfo;563 fn create_multiple_items(564 &self,565 sender: T::CrossAccountId,566 to: T::CrossAccountId,567 data: Vec<CreateItemData>,568 ) -> DispatchResultWithPostInfo;569 fn burn_item(570 &self,571 sender: T::CrossAccountId,572 token: TokenId,573 amount: u128,574 ) -> DispatchResultWithPostInfo;575576 fn transfer(577 &self,578 sender: T::CrossAccountId,579 to: T::CrossAccountId,580 token: TokenId,581 amount: u128,582 ) -> DispatchResultWithPostInfo;583 fn approve(584 &self,585 sender: T::CrossAccountId,586 spender: T::CrossAccountId,587 token: TokenId,588 amount: u128,589 ) -> DispatchResultWithPostInfo;590 fn transfer_from(591 &self,592 sender: T::CrossAccountId,593 from: T::CrossAccountId,594 to: T::CrossAccountId,595 token: TokenId,596 amount: u128,597 ) -> DispatchResultWithPostInfo;598 fn burn_from(599 &self,600 sender: T::CrossAccountId,601 from: T::CrossAccountId,602 token: TokenId,603 amount: u128,604 ) -> DispatchResultWithPostInfo;605606 fn set_variable_metadata(607 &self,608 sender: T::CrossAccountId,609 token: TokenId,610 data: Vec<u8>,611 ) -> DispatchResultWithPostInfo;612613 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;614 fn token_exists(&self, token: TokenId) -> bool;615 fn last_token_id(&self) -> TokenId;616617 fn token_owner(&self, token: TokenId) -> T::CrossAccountId;618 fn const_metadata(&self, token: TokenId) -> Vec<u8>;619 fn variable_metadata(&self, token: TokenId) -> Vec<u8>;620621 /// How many tokens collection contains (Applicable to nonfungible/refungible)622 fn collection_tokens(&self) -> u32;623 /// Amount of different tokens account has (Applicable to nonfungible/refungible)624 fn account_balance(&self, account: T::CrossAccountId) -> u32;625 /// Amount of specific token account have (Applicable to fungible/refungible)626 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;627 fn allowance(628 &self,629 sender: T::CrossAccountId,630 spender: T::CrossAccountId,631 token: TokenId,632 ) -> u128;633}634635// Flexible enough for implementing CommonCollectionOperations636pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {637 let post_info = PostDispatchInfo {638 actual_weight: Some(weight),639 pays_fee: Pays::Yes,640 };641 match res {642 Ok(()) => Ok(post_info),643 Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),644 }645}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> {