difftreelog
feat allow more fields to be set on collection creation
in: master
7 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- 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<T: Config> Pallet<T> {
- pub fn init_collection(data: Collection<T::AccountId>) -> Result<CollectionId, DispatchError> {
+ pub fn init_collection(
+ owner: T::AccountId,
+ data: CreateCollectionData<T::AccountId>,
+ ) -> Result<CollectionId, DispatchError> {
{
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 @@
),
);
<T as Config>::Currency::settle(
- &data.owner,
+ &owner,
imbalance,
WithdrawReasons::TRANSFER,
ExistenceRequirement::KeepAlive,
@@ -443,12 +476,8 @@
}
<CreatedCollectionCount<T>>::put(created_count);
- <Pallet<T>>::deposit_event(Event::CollectionCreated(
- id,
- data.mode.id(),
- data.owner.clone(),
- ));
- <CollectionById<T>>::insert(id, data);
+ <Pallet<T>>::deposit_event(Event::CollectionCreated(id, data.mode.id(), owner.clone()));
+ <CollectionById<T>>::insert(id, collection);
Ok(id)
}
@@ -532,6 +561,61 @@
Ok(())
}
+
+ pub fn clamp_limits(
+ mode: CollectionMode,
+ old_limit: &CollectionLimits,
+ mut new_limit: CollectionLimits,
+ ) -> Result<CollectionLimits, DispatchError> {
+ 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 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,
+ <Error<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 => {},
+ );
+ Ok(new_limit)
+ }
}
#[macro_export]
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.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error, decl_event,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::DispatchResultWithPostInfo,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use up_data_structs::{38 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, CUSTOM_DATA_LIMIT,39 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, OFFCHAIN_SCHEMA_LIMIT,40 FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,41 NFT_SPONSOR_TRANSFER_TIMEOUT, AccessMode, Collection, CreateItemData, CollectionLimits,42 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,43};44use pallet_common::{45 account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,46 CommonWeightInfo,47};48use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};49use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};50use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5152#[cfg(test)]53mod mock;5455#[cfg(test)]56mod tests;5758mod eth;59mod sponsorship;60pub use sponsorship::UniqueSponsorshipHandler;61pub use eth::sponsoring::UniqueEthSponsorshipHandler;6263pub use eth::UniqueErcSupport;6465pub mod common;66use common::CommonWeights;67pub mod dispatch;68use dispatch::dispatch_call;6970#[cfg(feature = "runtime-benchmarks")]71mod benchmarking;72pub mod weights;73use weights::WeightInfo;7475decl_error! {76 /// Error for non-fungible-token module.77 pub enum Error for Module<T: Config> {78 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.79 CollectionDecimalPointLimitExceeded,80 /// This address is not set as sponsor, use setCollectionSponsor first.81 ConfirmUnsetSponsorFail,82 /// Length of items properties must be greater than 0.83 EmptyArgument,84 /// Collection limit bounds per collection exceeded85 CollectionLimitBoundsExceeded,86 /// Tried to enable permissions which are only permitted to be disabled87 OwnerPermissionsCantBeReverted,88 }89}9091pub trait Config:92 system::Config93 + pallet_evm_coder_substrate::Config94 + pallet_common::Config95 + pallet_nonfungible::Config96 + pallet_refungible::Config97 + pallet_fungible::Config98 + Sized99 + TypeInfo100{101 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;102103 /// Weight information for extrinsics in this pallet.104 type WeightInfo: WeightInfo;105}106107decl_event! {108 pub enum Event<T>109 where110 <T as frame_system::Config>::AccountId,111 <T as pallet_common::Config>::CrossAccountId,112 {113 /// Collection sponsor was removed114 ///115 /// # Arguments116 ///117 /// * collection_id: Globally unique collection identifier.118 CollectionSponsorRemoved(CollectionId),119120 /// Collection admin was added121 ///122 /// # Arguments123 ///124 /// * collection_id: Globally unique collection identifier.125 ///126 /// * admin: Admin address.127 CollectionAdminAdded(CollectionId, CrossAccountId),128129 /// Collection owned was change130 ///131 /// # Arguments132 ///133 /// * collection_id: Globally unique collection identifier.134 ///135 /// * owner: New owner address.136 CollectionOwnedChanged(CollectionId, AccountId),137138 /// Collection sponsor was set139 ///140 /// # Arguments141 ///142 /// * collection_id: Globally unique collection identifier.143 ///144 /// * owner: New sponsor address.145 CollectionSponsorSet(CollectionId, AccountId),146147 /// const on chain schema was set148 ///149 /// # Arguments150 ///151 /// * collection_id: Globally unique collection identifier.152 ConstOnChainSchemaSet(CollectionId),153154 /// New sponsor was confirm155 ///156 /// # Arguments157 ///158 /// * collection_id: Globally unique collection identifier.159 ///160 /// * sponsor: New sponsor address.161 SponsorshipConfirmed(CollectionId, AccountId),162163 /// Collection admin was removed164 ///165 /// # Arguments166 ///167 /// * collection_id: Globally unique collection identifier.168 ///169 /// * admin: Admin address.170 CollectionAdminRemoved(CollectionId, CrossAccountId),171172 /// Address was remove from allow list173 ///174 /// # Arguments175 ///176 /// * collection_id: Globally unique collection identifier.177 ///178 /// * user: Address.179 AllowListAddressRemoved(CollectionId, CrossAccountId),180181 /// Address was add to allow list182 ///183 /// # Arguments184 ///185 /// * collection_id: Globally unique collection identifier.186 ///187 /// * user: Address.188 AllowListAddressAdded(CollectionId, CrossAccountId),189190 /// Collection limits was set191 ///192 /// # Arguments193 ///194 /// * collection_id: Globally unique collection identifier.195 CollectionLimitSet(CollectionId),196197 /// Mint permission was set198 ///199 /// # Arguments200 ///201 /// * collection_id: Globally unique collection identifier.202 MintPermissionSet(CollectionId),203204 /// Offchain schema was set205 ///206 /// # Arguments207 ///208 /// * collection_id: Globally unique collection identifier.209 OffchainSchemaSet(CollectionId),210211 /// Public access mode was set212 ///213 /// # Arguments214 ///215 /// * collection_id: Globally unique collection identifier.216 ///217 /// * mode: New access state.218 PublicAccessModeSet(CollectionId, AccessMode),219220 /// Schema version was set221 ///222 /// # Arguments223 ///224 /// * collection_id: Globally unique collection identifier.225 SchemaVersionSet(CollectionId),226227 /// Variable on chain schema was set228 ///229 /// # Arguments230 ///231 /// * collection_id: Globally unique collection identifier.232 VariableOnChainSchemaSet(CollectionId),233 }234}235236type SelfWeightOf<T> = <T as Config>::WeightInfo;237238// # Used definitions239//240// ## User control levels241//242// chain-controlled - key is uncontrolled by user243// i.e autoincrementing index244// can use non-cryptographic hash245// real - key is controlled by user246// but it is hard to generate enough colliding values, i.e owner of signed txs247// can use non-cryptographic hash248// controlled - key is completly controlled by users249// i.e maps with mutable keys250// should use cryptographic hash251//252// ## User control level downgrade reasons253//254// ?1 - chain-controlled -> controlled255// collections/tokens can be destroyed, resulting in massive holes256// ?2 - chain-controlled -> controlled257// same as ?1, but can be only added, resulting in easier exploitation258// ?3 - real -> controlled259// no confirmation required, so addresses can be easily generated260decl_storage! {261 trait Store for Module<T: Config> as Unique {262263 //#region Private members264 /// Used for migrations265 ChainVersion: u64;266 //#endregion267268 //#region Tokens transfer rate limit baskets269 /// (Collection id (controlled?2), who created (real))270 /// TODO: Off chain worker should remove from this map when collection gets removed271 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;272 /// Collection id (controlled?2), token id (controlled?2)273 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;274 /// Collection id (controlled?2), owning user (real)275 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;276 /// Collection id (controlled?2), token id (controlled?2)277 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;278 //#endregion279280 /// Variable metadata sponsoring281 /// Collection id (controlled?2), token id (controlled?2)282 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;283 /// Approval sponsoring284 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;285 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;286 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;287 }288}289290decl_module! {291 pub struct Module<T: Config> for enum Call292 where293 origin: T::Origin294 {295 type Error = Error<T>;296297 fn deposit_event() = default;298299 fn on_initialize(_now: T::BlockNumber) -> Weight {300 0301 }302303 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.304 ///305 /// # Permissions306 ///307 /// * Anyone.308 ///309 /// # Arguments310 ///311 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.312 ///313 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.314 ///315 /// * token_prefix: UTF-8 string with token prefix.316 ///317 /// * mode: [CollectionMode] collection type and type dependent data.318 // returns collection ID319 #[weight = <SelfWeightOf<T>>::create_collection()]320 #[transactional]321 pub fn create_collection(origin,322 collection_name: Vec<u16>,323 collection_description: Vec<u16>,324 token_prefix: Vec<u8>,325 mode: CollectionMode) -> DispatchResult {326327 // Anyone can create a collection328 let who = ensure_signed(origin)?;329330 // Create new collection331 let new_collection = Collection {332 owner: who,333 name: collection_name,334 mode: mode.clone(),335 mint_mode: false,336 access: AccessMode::Normal,337 description: collection_description,338 token_prefix,339 offchain_schema: Vec::new(),340 schema_version: SchemaVersion::ImageURL,341 sponsorship: SponsorshipState::Disabled,342 variable_on_chain_schema: Vec::new(),343 const_on_chain_schema: Vec::new(),344 limits: Default::default(),345 meta_update_permission: Default::default(),346 };347348 let _id = match mode {349 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(new_collection)?},350 CollectionMode::Fungible(decimal_points) => {351 // check params352 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);353 <PalletFungible<T>>::init_collection(new_collection)?354 }355 CollectionMode::ReFungible => {356 <PalletRefungible<T>>::init_collection(new_collection)?357 }358 };359360 Ok(())361 }362363 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.364 ///365 /// # Permissions366 ///367 /// * Collection Owner.368 ///369 /// # Arguments370 ///371 /// * collection_id: collection to destroy.372 #[weight = <SelfWeightOf<T>>::destroy_collection()]373 #[transactional]374 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {375 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);376377 let collection = <CollectionHandle<T>>::try_get(collection_id)?;378 collection.check_is_owner(&sender)?;379380 // =========381382 match collection.mode {383 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,384 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,385 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,386 }387388 <NftTransferBasket<T>>::remove_prefix(collection_id, None);389 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);390 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);391392 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);393 <NftApproveBasket<T>>::remove_prefix(collection_id, None);394 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);395 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);396397 Ok(())398 }399400 /// Add an address to allow list.401 ///402 /// # Permissions403 ///404 /// * Collection Owner405 /// * Collection Admin406 ///407 /// # Arguments408 ///409 /// * collection_id.410 ///411 /// * address.412 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]413 #[transactional]414 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{415416 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);417 let collection = <CollectionHandle<T>>::try_get(collection_id)?;418419 <PalletCommon<T>>::toggle_allowlist(420 &collection,421 &sender,422 &address,423 true,424 )?;425426 Self::deposit_event(Event::<T>::AllowListAddressAdded(427 collection_id,428 address429 ));430431 Ok(())432 }433434 /// Remove an address from allow list.435 ///436 /// # Permissions437 ///438 /// * Collection Owner439 /// * Collection Admin440 ///441 /// # Arguments442 ///443 /// * collection_id.444 ///445 /// * address.446 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]447 #[transactional]448 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{449450 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);451 let collection = <CollectionHandle<T>>::try_get(collection_id)?;452453 <PalletCommon<T>>::toggle_allowlist(454 &collection,455 &sender,456 &address,457 false,458 )?;459460 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(461 collection_id,462 address463 ));464465 Ok(())466 }467468 /// Toggle between normal and allow list access for the methods with access for `Anyone`.469 ///470 /// # Permissions471 ///472 /// * Collection Owner.473 ///474 /// # Arguments475 ///476 /// * collection_id.477 ///478 /// * mode: [AccessMode]479 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]480 #[transactional]481 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult482 {483 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);484485 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;486 target_collection.check_is_owner(&sender)?;487488 target_collection.access = mode.clone();489490 <Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(491 collection_id,492 mode493 ));494495 target_collection.save()496 }497498 /// Allows Anyone to create tokens if:499 /// * Allow List is enabled, and500 /// * Address is added to allow list, and501 /// * This method was called with True parameter502 ///503 /// # Permissions504 /// * Collection Owner505 ///506 /// # Arguments507 ///508 /// * collection_id.509 ///510 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.511 #[weight = <SelfWeightOf<T>>::set_mint_permission()]512 #[transactional]513 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult514 {515 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);516517 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;518 target_collection.check_is_owner(&sender)?;519520 target_collection.mint_mode = mint_permission;521522 <Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(523 collection_id524 ));525526 target_collection.save()527 }528529 /// Change the owner of the collection.530 ///531 /// # Permissions532 ///533 /// * Collection Owner.534 ///535 /// # Arguments536 ///537 /// * collection_id.538 ///539 /// * new_owner.540 #[weight = <SelfWeightOf<T>>::change_collection_owner()]541 #[transactional]542 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {543544 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);545546 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;547 target_collection.check_is_owner(&sender)?;548549 target_collection.owner = new_owner.clone();550 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(551 collection_id,552 new_owner553 ));554555 target_collection.save()556 }557558 /// Adds an admin of the Collection.559 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.560 ///561 /// # Permissions562 ///563 /// * Collection Owner.564 /// * Collection Admin.565 ///566 /// # Arguments567 ///568 /// * collection_id: ID of the Collection to add admin for.569 ///570 /// * new_admin_id: Address of new admin to add.571 #[weight = <SelfWeightOf<T>>::add_collection_admin()]572 #[transactional]573 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {574 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);575 let collection = <CollectionHandle<T>>::try_get(collection_id)?;576577 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(578 collection_id,579 new_admin_id.clone()580 ));581582 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)583 }584585 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.586 ///587 /// # Permissions588 ///589 /// * Collection Owner.590 /// * Collection Admin.591 ///592 /// # Arguments593 ///594 /// * collection_id: ID of the Collection to remove admin for.595 ///596 /// * account_id: Address of admin to remove.597 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]598 #[transactional]599 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let collection = <CollectionHandle<T>>::try_get(collection_id)?;602603 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(604 collection_id,605 account_id.clone()606 ));607608 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)609 }610611 /// # Permissions612 ///613 /// * Collection Owner614 ///615 /// # Arguments616 ///617 /// * collection_id.618 ///619 /// * new_sponsor.620 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]621 #[transactional]622 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {623 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);624625 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;626 target_collection.check_is_owner(&sender)?;627628 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());629630 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(631 collection_id,632 new_sponsor633 ));634635 target_collection.save()636 }637638 /// # Permissions639 ///640 /// * Sponsor.641 ///642 /// # Arguments643 ///644 /// * collection_id.645 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]646 #[transactional]647 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {648 let sender = ensure_signed(origin)?;649650 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;651 ensure!(652 target_collection.sponsorship.pending_sponsor() == Some(&sender),653 Error::<T>::ConfirmUnsetSponsorFail654 );655656 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());657658 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(659 collection_id,660 sender661 ));662663 target_collection.save()664 }665666 /// Switch back to pay-per-own-transaction model.667 ///668 /// # Permissions669 ///670 /// * Collection owner.671 ///672 /// # Arguments673 ///674 /// * collection_id.675 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]676 #[transactional]677 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {678 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);679680 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;681 target_collection.check_is_owner(&sender)?;682683 target_collection.sponsorship = SponsorshipState::Disabled;684685 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(686 collection_id687 ));688 target_collection.save()689 }690691 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.692 ///693 /// # Permissions694 ///695 /// * Collection Owner.696 /// * Collection Admin.697 /// * Anyone if698 /// * Allow List is enabled, and699 /// * Address is added to allow list, and700 /// * MintPermission is enabled (see SetMintPermission method)701 ///702 /// # Arguments703 ///704 /// * collection_id: ID of the collection.705 ///706 /// * owner: Address, initial owner of the NFT.707 ///708 /// * data: Token data to store on chain.709 #[weight = <CommonWeights<T>>::create_item()]710 #[transactional]711 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {712 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);713714 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))715 }716717 /// This method creates multiple items in a collection created with CreateCollection method.718 ///719 /// # Permissions720 ///721 /// * Collection Owner.722 /// * Collection Admin.723 /// * Anyone if724 /// * Allow List is enabled, and725 /// * Address is added to allow list, and726 /// * MintPermission is enabled (see SetMintPermission method)727 ///728 /// # Arguments729 ///730 /// * collection_id: ID of the collection.731 ///732 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].733 ///734 /// * owner: Address, initial owner of the NFT.735 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]736 #[transactional]737 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {738 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);739 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);740741 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))742 }743744 // TODO! transaction weight745746 /// Set transfers_enabled value for particular collection747 ///748 /// # Permissions749 ///750 /// * Collection Owner.751 ///752 /// # Arguments753 ///754 /// * collection_id: ID of the collection.755 ///756 /// * value: New flag value.757 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]758 #[transactional]759 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {760 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);761 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;762 target_collection.check_is_owner(&sender)?;763764 // =========765766 target_collection.limits.transfers_enabled = Some(value);767 target_collection.save()768 }769770 /// Destroys a concrete instance of NFT.771 ///772 /// # Permissions773 ///774 /// * Collection Owner.775 /// * Collection Admin.776 /// * Current NFT Owner.777 ///778 /// # Arguments779 ///780 /// * collection_id: ID of the collection.781 ///782 /// * item_id: ID of NFT to burn.783 #[weight = <CommonWeights<T>>::burn_item()]784 #[transactional]785 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {786 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);787788 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;789 if value == 1 {790 <NftTransferBasket<T>>::remove(collection_id, item_id);791 <NftApproveBasket<T>>::remove(collection_id, item_id);792 }793 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?794 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());795 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));796 Ok(post_info)797 }798799 /// Destroys a concrete instance of NFT on behalf of the owner800 /// See also: [`approve`]801 ///802 /// # Permissions803 ///804 /// * Collection Owner.805 /// * Collection Admin.806 /// * Current NFT Owner.807 ///808 /// # Arguments809 ///810 /// * collection_id: ID of the collection.811 ///812 /// * item_id: ID of NFT to burn.813 ///814 /// * from: owner of item815 #[weight = <CommonWeights<T>>::burn_from()]816 #[transactional]817 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {818 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);819820 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))821 }822823 /// Change ownership of the token.824 ///825 /// # Permissions826 ///827 /// * Collection Owner828 /// * Collection Admin829 /// * Current NFT owner830 ///831 /// # Arguments832 ///833 /// * recipient: Address of token recipient.834 ///835 /// * collection_id.836 ///837 /// * item_id: ID of the item838 /// * Non-Fungible Mode: Required.839 /// * Fungible Mode: Ignored.840 /// * Re-Fungible Mode: Required.841 ///842 /// * value: Amount to transfer.843 /// * Non-Fungible Mode: Ignored844 /// * Fungible Mode: Must specify transferred amount845 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)846 #[weight = <CommonWeights<T>>::transfer()]847 #[transactional]848 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {849 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);850851 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))852 }853854 /// Set, change, or remove approved address to transfer the ownership of the NFT.855 ///856 /// # Permissions857 ///858 /// * Collection Owner859 /// * Collection Admin860 /// * Current NFT owner861 ///862 /// # Arguments863 ///864 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).865 ///866 /// * collection_id.867 ///868 /// * item_id: ID of the item.869 #[weight = <CommonWeights<T>>::approve()]870 #[transactional]871 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {872 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);873874 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))875 }876877 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.878 ///879 /// # Permissions880 /// * Collection Owner881 /// * Collection Admin882 /// * Current NFT owner883 /// * Address approved by current NFT owner884 ///885 /// # Arguments886 ///887 /// * from: Address that owns token.888 ///889 /// * recipient: Address of token recipient.890 ///891 /// * collection_id.892 ///893 /// * item_id: ID of the item.894 ///895 /// * value: Amount to transfer.896 #[weight = <CommonWeights<T>>::transfer_from()]897 #[transactional]898 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {899 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900901 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))902 }903904 /// Set off-chain data schema.905 ///906 /// # Permissions907 ///908 /// * Collection Owner909 /// * Collection Admin910 ///911 /// # Arguments912 ///913 /// * collection_id.914 ///915 /// * schema: String representing the offchain data schema.916 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]917 #[transactional]918 pub fn set_variable_meta_data (919 origin,920 collection_id: CollectionId,921 item_id: TokenId,922 data: Vec<u8>923 ) -> DispatchResultWithPostInfo {924 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);925926 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))927 }928929 /// Set meta_update_permission value for particular collection930 ///931 /// # Permissions932 ///933 /// * Collection Owner.934 ///935 /// # Arguments936 ///937 /// * collection_id: ID of the collection.938 ///939 /// * value: New flag value.940 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]941 #[transactional]942 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {943 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);944 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;945946 ensure!(947 target_collection.meta_update_permission != MetaUpdatePermission::None,948 <CommonError<T>>::MetadataFlagFrozen,949 );950 target_collection.check_is_owner(&sender)?;951952 target_collection.meta_update_permission = value;953954 target_collection.save()955 }956957 /// Set schema standard958 /// ImageURL959 /// Unique960 ///961 /// # Permissions962 ///963 /// * Collection Owner964 /// * Collection Admin965 ///966 /// # Arguments967 ///968 /// * collection_id.969 ///970 /// * schema: SchemaVersion: enum971 #[weight = <SelfWeightOf<T>>::set_schema_version()]972 #[transactional]973 pub fn set_schema_version(974 origin,975 collection_id: CollectionId,976 version: SchemaVersion977 ) -> DispatchResult {978 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);979 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;980 target_collection.check_is_owner_or_admin(&sender)?;981 target_collection.schema_version = version;982983 <Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(984 collection_id985 ));986987 target_collection.save()988 }989990 /// Set off-chain data schema.991 ///992 /// # Permissions993 ///994 /// * Collection Owner995 /// * Collection Admin996 ///997 /// # Arguments998 ///999 /// * collection_id.1000 ///1001 /// * schema: String representing the offchain data schema.1002 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1003 #[transactional]1004 pub fn set_offchain_schema(1005 origin,1006 collection_id: CollectionId,1007 schema: Vec<u8>1008 ) -> DispatchResult {1009 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1010 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1011 target_collection.check_is_owner_or_admin(&sender)?;10121013 // check schema limit1014 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");10151016 target_collection.offchain_schema = schema;10171018 <Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1019 collection_id1020 ));10211022 target_collection.save()1023 }10241025 /// Set const on-chain data schema.1026 ///1027 /// # Permissions1028 ///1029 /// * Collection Owner1030 /// * Collection Admin1031 ///1032 /// # Arguments1033 ///1034 /// * collection_id.1035 ///1036 /// * schema: String representing the const on-chain data schema.1037 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1038 #[transactional]1039 pub fn set_const_on_chain_schema (1040 origin,1041 collection_id: CollectionId,1042 schema: Vec<u8>1043 ) -> DispatchResult {1044 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1045 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1046 target_collection.check_is_owner_or_admin(&sender)?;10471048 // check schema limit1049 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");10501051 target_collection.const_on_chain_schema = schema;10521053 <Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1054 collection_id1055 ));10561057 target_collection.save()1058 }10591060 /// Set variable on-chain data schema.1061 ///1062 /// # Permissions1063 ///1064 /// * Collection Owner1065 /// * Collection Admin1066 ///1067 /// # Arguments1068 ///1069 /// * collection_id.1070 ///1071 /// * schema: String representing the variable on-chain data schema.1072 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1073 #[transactional]1074 pub fn set_variable_on_chain_schema (1075 origin,1076 collection_id: CollectionId,1077 schema: Vec<u8>1078 ) -> DispatchResult {1079 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1080 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1081 target_collection.check_is_owner_or_admin(&sender)?;10821083 // check schema limit1084 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");10851086 target_collection.variable_on_chain_schema = schema;10871088 <Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1089 collection_id1090 ));10911092 target_collection.save()1093 }10941095 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1096 #[transactional]1097 pub fn set_collection_limits(1098 origin,1099 collection_id: CollectionId,1100 new_limit: CollectionLimits,1101 ) -> DispatchResult {1102 let mut new_limit = new_limit;1103 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1104 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1105 target_collection.check_is_owner(&sender)?;1106 let old_limit = &target_collection.limits;11071108 macro_rules! limit_default {1109 ($old:ident, $new:ident, $($field:ident $(($arg:expr))? => $check:expr),* $(,)?) => {{1110 $(1111 if let Some($new) = $new.$field {1112 let $old = $old.$field($($arg)?);1113 let _ = $new;1114 let _ = $old;1115 $check1116 } else {1117 $new.$field = $old.$field1118 }1119 )*1120 }};1121 }11221123 limit_default!(old_limit, new_limit,1124 account_token_ownership_limit => ensure!(1125 new_limit <= MAX_TOKEN_OWNERSHIP,1126 <Error<T>>::CollectionLimitBoundsExceeded,1127 ),1128 sponsor_transfer_timeout(match target_collection.mode {1129 CollectionMode::NFT => NFT_SPONSOR_TRANSFER_TIMEOUT,1130 CollectionMode::Fungible(_) => FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1131 CollectionMode::ReFungible => REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,1132 }) => ensure!(1133 new_limit <= MAX_SPONSOR_TIMEOUT,1134 <Error<T>>::CollectionLimitBoundsExceeded,1135 ),1136 sponsored_data_size => ensure!(1137 new_limit <= CUSTOM_DATA_LIMIT,1138 <Error<T>>::CollectionLimitBoundsExceeded,1139 ),1140 token_limit => ensure!(1141 old_limit >= new_limit && new_limit > 0,1142 <CommonError<T>>::CollectionTokenLimitExceeded1143 ),1144 owner_can_transfer => ensure!(1145 old_limit || !new_limit,1146 <Error<T>>::OwnerPermissionsCantBeReverted,1147 ),1148 owner_can_destroy => ensure!(1149 old_limit || !new_limit,1150 <Error<T>>::OwnerPermissionsCantBeReverted,1151 ),1152 sponsored_data_rate_limit => {},1153 transfers_enabled => {},1154 );11551156 target_collection.limits = new_limit;11571158 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1159 collection_id1160 ));11611162 target_collection.save()1163 }1164 }1165}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> {