From e637581b59d052f81a5566895cfcfda9c01fae8b Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 11 Jan 2022 19:00:17 +0000 Subject: [PATCH] feat: allow more fields to be set on collection creation --- --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -14,7 +14,10 @@ COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH, COLLECTION_ADMINS_LIMIT, MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, - WithdrawReasons, CollectionStats, + WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode, + NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, + REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits, + CreateCollectionData, SponsorshipState, }; pub use pallet::*; use sp_core::H160; @@ -282,6 +285,10 @@ TokenVariableDataLimitExceeded, /// Exceeded max admin count CollectionAdminCountExceeded, + /// Collection limit bounds per collection exceeded + CollectionLimitBoundsExceeded, + /// Tried to enable permissions which are only permitted to be disabled + OwnerPermissionsCantBeReverted, /// Collection settings not allowing items transferring TransferNotAllowed, @@ -392,7 +399,10 @@ } impl Pallet { - pub fn init_collection(data: Collection) -> Result { + pub fn init_collection( + owner: T::AccountId, + data: CreateCollectionData, + ) -> Result { { ensure!( data.name.len() <= MAX_COLLECTION_NAME_LENGTH, @@ -423,6 +433,29 @@ // ========= + let collection = Collection { + owner: owner.clone(), + name: data.name, + mode: data.mode.clone(), + mint_mode: false, + access: data.access.unwrap_or_default(), + description: data.description, + token_prefix: data.token_prefix, + offchain_schema: data.offchain_schema, + schema_version: data.schema_version.unwrap_or_default(), + sponsorship: data + .pending_sponsor + .map(SponsorshipState::Unconfirmed) + .unwrap_or_default(), + variable_on_chain_schema: data.variable_on_chain_schema, + const_on_chain_schema: data.const_on_chain_schema, + limits: data + .limits + .map(|limits| Self::clamp_limits(data.mode.clone(), &Default::default(), limits)) + .unwrap_or_else(|| Ok(CollectionLimits::default()))?, + meta_update_permission: data.meta_update_permission.unwrap_or_default(), + }; + // Take a (non-refundable) deposit of collection creation { let mut imbalance = @@ -434,7 +467,7 @@ ), ); ::Currency::settle( - &data.owner, + &owner, imbalance, WithdrawReasons::TRANSFER, ExistenceRequirement::KeepAlive, @@ -443,12 +476,8 @@ } >::put(created_count); - >::deposit_event(Event::CollectionCreated( - id, - data.mode.id(), - data.owner.clone(), - )); - >::insert(id, data); + >::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone())); + >::insert(id, collection); Ok(id) } @@ -532,6 +561,61 @@ Ok(()) } + + pub fn clamp_limits( + mode: CollectionMode, + old_limit: &CollectionLimits, + mut new_limit: CollectionLimits, + ) -> Result { + 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, + >::CollectionLimitBoundsExceeded, + ), + sponsor_transfer_timeout(match 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, + >::CollectionLimitBoundsExceeded, + ), + sponsored_data_size => ensure!( + new_limit <= CUSTOM_DATA_LIMIT, + >::CollectionLimitBoundsExceeded, + ), + token_limit => ensure!( + old_limit >= new_limit && new_limit > 0, + >::CollectionTokenLimitExceeded + ), + owner_can_transfer => ensure!( + old_limit || !new_limit, + >::OwnerPermissionsCantBeReverted, + ), + owner_can_destroy => ensure!( + old_limit || !new_limit, + >::OwnerPermissionsCantBeReverted, + ), + sponsored_data_rate_limit => {}, + transfers_enabled => {}, + ); + Ok(new_limit) + } } #[macro_export] --- 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 Pallet { - pub fn init_collection(data: Collection) -> Result { - >::init_collection(data) + pub fn init_collection( + owner: T::AccountId, + data: CreateCollectionData, + ) -> Result { + >::init_collection(owner, data) } pub fn destroy_collection( collection: FungibleHandle, --- 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 Pallet { - pub fn init_collection(data: Collection) -> Result { - >::init_collection(data) + pub fn init_collection( + owner: T::AccountId, + data: CreateCollectionData, + ) -> Result { + >::init_collection(owner, data) } pub fn destroy_collection( collection: NonfungibleHandle, --- 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 Pallet { - pub fn init_collection(data: Collection) -> Result { - >::init_collection(data) + pub fn init_collection( + owner: T::AccountId, + data: CreateCollectionData, + ) -> Result { + >::init_collection(owner, data) } pub fn destroy_collection( collection: RefungibleHandle, --- 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 = >::create_collection()] #[transactional] + #[deprecated] pub fn create_collection(origin, collection_name: Vec, collection_description: Vec, token_prefix: Vec, 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 = >::create_collection()] + #[transactional] + pub fn create_collection_ex(origin, data: CreateCollectionData) -> DispatchResult { + let owner = ensure_signed(origin)?; - let _id = match mode { - CollectionMode::NFT => {>::init_collection(new_collection)?}, + let _id = match data.mode { + CollectionMode::NFT => {>::init_collection(owner, data)?}, CollectionMode::Fungible(decimal_points) => { // check params ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::::CollectionDecimalPointLimitExceeded); - >::init_collection(new_collection)? + >::init_collection(owner, data)? } CollectionMode::ReFungible => { - >::init_collection(new_collection)? + >::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 = >::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, - >::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, - >::CollectionLimitBoundsExceeded, - ), - sponsored_data_size => ensure!( - new_limit <= CUSTOM_DATA_LIMIT, - >::CollectionLimitBoundsExceeded, - ), - token_limit => ensure!( - old_limit >= new_limit && new_limit > 0, - >::CollectionTokenLimitExceeded - ), - owner_can_transfer => ensure!( - old_limit || !new_limit, - >::OwnerPermissionsCantBeReverted, - ), - owner_can_destroy => ensure!( - old_limit || !new_limit, - >::OwnerPermissionsCantBeReverted, - ), - sponsored_data_rate_limit => {}, - transfers_enabled => {}, - ); - - target_collection.limits = new_limit; + target_collection.limits = >::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?; >::deposit_event(Event::::CollectionLimitSet( collection_id --- 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; --- 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 { + #[derivative(Default(value = "CollectionMode::NFT"))] + pub mode: CollectionMode, + pub access: Option, + pub name: Vec, + pub description: Vec, + pub token_prefix: Vec, + pub offchain_schema: Vec, + pub schema_version: Option, + pub pending_sponsor: Option, + pub limits: Option, + pub variable_on_chain_schema: Vec, + pub const_on_chain_schema: Vec, + pub meta_update_permission: Option, +} + #[derive(Encode, Decode, Debug, Clone, PartialEq, TypeInfo)] #[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))] pub struct NftItemType { -- gitstuff