difftreelog
added consts to `Unique` pallet
in: master
6 files changed
pallets/unique/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617//! # Unique Pallet18//!19//! A pallet governing Unique transactions.20//!21//! - [`Config`]22//! - [`Call`]23//! - [`Pallet`]24//!25//! ## Overview26//!27//! The Unique pallet's purpose is to be the primary interface between28//! external users and the inner structure of the Unique chains.29//!30//! It also contains an implementation of [`CollectionHelpers`][`eth`],31//! an Ethereum contract dealing with collection operations.32//!33//! ## Interface34//!35//! ### Dispatchables36//!37//! - `create_collection` - Create a collection of tokens. **Deprecated**, use `create_collection_ex`.38//! - `create_collection_ex` - Create a collection of tokens with explicit parameters.39//! - `destroy_collection` - Destroy a collection if no tokens exist within.40//! - `add_to_allow_list` - Add an address to allow list.41//! - `remove_from_allow_list` - Remove an address from allow list.42//! - `change_collection_owner` - Change the owner of the collection.43//! - `add_collection_admin` - Add an admin to a collection.44//! - `remove_collection_admin` - Remove admin of a collection.45//! - `set_collection_sponsor` - Invite a new collection sponsor.46//! - `confirm_sponsorship` - Confirm own sponsorship of a collection, becoming the sponsor.47//! - `remove_collection_sponsor` - Remove a sponsor from a collection.48//! - `create_item` - Create an item within a collection.49//! - `create_multiple_items` - Create multiple items within a collection.50//! - `set_collection_properties` - Add or change collection properties.51//! - `delete_collection_properties` - Delete specified collection properties.52//! - `set_token_properties` - Add or change token properties.53//! - `delete_token_properties` - Delete token properties.54//! - `set_token_property_permissions` - Add or change token property permissions of a collection.55//! - `create_multiple_items_ex` - Create multiple items within a collection with explicitly specified initial parameters.56//! - `set_transfers_enabled_flag` - Completely allow or disallow transfers for a particular collection.57//! - `burn_item` - Destroy an item.58//! - `burn_from` - Destroy an item on behalf of the owner as a non-owner account.59//! - `transfer` - Change ownership of the token.60//! - `transfer_from` - Change ownership of the token on behalf of the owner as a non-owner account.61//! - `approve` - Allow a non-permissioned address to transfer or burn an item.62//! - `set_collection_limits` - Set specific limits of a collection.63//! - `set_collection_permissions` - Set specific permissions of a collection.64//! - `repartition` - Re-partition a refungible token, while owning all of its parts.6566#![recursion_limit = "1024"]67#![cfg_attr(not(feature = "std"), no_std)]68#![allow(69 clippy::too_many_arguments,70 clippy::unnecessary_mut_passed,71 clippy::unused_unit72)]7374extern crate alloc;7576use frame_support::{77 decl_module, decl_storage, decl_error, decl_event,78 dispatch::DispatchResult,79 ensure, fail,80 weights::{Weight},81 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},82 BoundedVec,83};84use scale_info::TypeInfo;85use frame_system::{self as system, ensure_signed};86use sp_std::{vec, vec::Vec};87use up_data_structs::{88 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,89 CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,90 SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,91 PropertyKeyPermission,92};93use pallet_evm::account::CrossAccountId;94use pallet_common::{95 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,96 dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,97};98pub mod eth;99100#[cfg(feature = "runtime-benchmarks")]101pub mod benchmarking;102pub mod weights;103use weights::WeightInfo;104105/// Maximum number of levels of depth in the token nesting tree.106pub const NESTING_BUDGET: u32 = 5;107108decl_error! {109 /// Errors for the common Unique transactions.110 pub enum Error for Module<T: Config> {111 /// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].112 CollectionDecimalPointLimitExceeded,113 /// This address is not set as sponsor, use setCollectionSponsor first.114 ConfirmUnsetSponsorFail,115 /// Length of items properties must be greater than 0.116 EmptyArgument,117 /// Repertition is only supported by refungible collection.118 RepartitionCalledOnNonRefungibleCollection,119 }120}121122/// Configuration trait of this pallet.123pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {124 /// Overarching event type.125 type RuntimeEvent: From<Event<Self>> + Into<<Self as frame_system::Config>::RuntimeEvent>;126127 /// Weight information for extrinsics in this pallet.128 type WeightInfo: WeightInfo;129130 /// Weight information for common pallet operations.131 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;132133 /// Weight info information for extra refungible pallet operations.134 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;135}136137decl_event! {138 pub enum Event<T>139 where140 <T as frame_system::Config>::AccountId,141 <T as pallet_evm::Config>::CrossAccountId,142 {143 /// Collection sponsor was removed144 ///145 /// # Arguments146 /// * collection_id: ID of the affected collection.147 CollectionSponsorRemoved(CollectionId),148149 /// Collection admin was added150 ///151 /// # Arguments152 /// * collection_id: ID of the affected collection.153 /// * admin: Admin address.154 CollectionAdminAdded(CollectionId, CrossAccountId),155156 /// Collection owned was changed157 ///158 /// # Arguments159 /// * collection_id: ID of the affected collection.160 /// * owner: New owner address.161 CollectionOwnedChanged(CollectionId, AccountId),162163 /// Collection sponsor was set164 ///165 /// # Arguments166 /// * collection_id: ID of the affected collection.167 /// * owner: New sponsor address.168 CollectionSponsorSet(CollectionId, AccountId),169170 /// New sponsor was confirm171 ///172 /// # Arguments173 /// * collection_id: ID of the affected collection.174 /// * sponsor: New sponsor address.175 SponsorshipConfirmed(CollectionId, AccountId),176177 /// Collection admin was removed178 ///179 /// # Arguments180 /// * collection_id: ID of the affected collection.181 /// * admin: Removed admin address.182 CollectionAdminRemoved(CollectionId, CrossAccountId),183184 /// Address was removed from the allow list185 ///186 /// # Arguments187 /// * collection_id: ID of the affected collection.188 /// * user: Address of the removed account.189 AllowListAddressRemoved(CollectionId, CrossAccountId),190191 /// Address was added to the allow list192 ///193 /// # Arguments194 /// * collection_id: ID of the affected collection.195 /// * user: Address of the added account.196 AllowListAddressAdded(CollectionId, CrossAccountId),197198 /// Collection limits were set199 ///200 /// # Arguments201 /// * collection_id: ID of the affected collection.202 CollectionLimitSet(CollectionId),203204 /// Collection permissions were set205 ///206 /// # Arguments207 /// * collection_id: ID of the affected collection.208 CollectionPermissionSet(CollectionId),209 }210}211212type SelfWeightOf<T> = <T as Config>::WeightInfo;213214// # Used definitions215//216// ## User control levels217//218// chain-controlled - key is uncontrolled by user219// i.e autoincrementing index220// can use non-cryptographic hash221// real - key is controlled by user222// but it is hard to generate enough colliding values, i.e owner of signed txs223// can use non-cryptographic hash224// controlled - key is completly controlled by users225// i.e maps with mutable keys226// should use cryptographic hash227//228// ## User control level downgrade reasons229//230// ?1 - chain-controlled -> controlled231// collections/tokens can be destroyed, resulting in massive holes232// ?2 - chain-controlled -> controlled233// same as ?1, but can be only added, resulting in easier exploitation234// ?3 - real -> controlled235// no confirmation required, so addresses can be easily generated236decl_storage! {237 trait Store for Module<T: Config> as Unique {238239 //#region Private members240 /// Used for migrations241 ChainVersion: u64;242 //#endregion243244 //#region Tokens transfer sponosoring rate limit baskets245 /// (Collection id (controlled?2), who created (real))246 /// TODO: Off chain worker should remove from this map when collection gets removed247 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;248 /// Collection id (controlled?2), token id (controlled?2)249 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250 /// Collection id (controlled?2), owning user (real)251 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;252 /// Collection id (controlled?2), token id (controlled?2)253 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>;254 //#endregion255256 /// Variable metadata sponsoring257 /// Collection id (controlled?2), token id (controlled?2)258 #[deprecated]259 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;260 /// Last sponsoring of token property setting // todo:doc rephrase this and the following261 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;262263 /// Last sponsoring of NFT approval in a collection264 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;265 /// Last sponsoring of fungible tokens approval in a collection266 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;267 /// Last sponsoring of RFT approval in a collection268 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>;269 }270}271272decl_module! {273 /// Type alias to Pallet, to be used by construct_runtime.274 pub struct Module<T: Config> for enum Call275 where276 origin: T::RuntimeOrigin277 {278 type Error = Error<T>;279280 pub fn deposit_event() = default;281282 fn on_initialize(_now: T::BlockNumber) -> Weight {283 Weight::zero()284 }285286 fn on_runtime_upgrade() -> Weight {287 Weight::zero()288 }289290 /// Create a collection of tokens.291 ///292 /// Each Token may have multiple properties encoded as an array of bytes293 /// of certain length. The initial owner of the collection is set294 /// to the address that signed the transaction and can be changed later.295 ///296 /// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.297 ///298 /// # Permissions299 ///300 /// * Anyone - becomes the owner of the new collection.301 ///302 /// # Arguments303 ///304 /// * `collection_name`: Wide-character string with collection name305 /// (limit [`MAX_COLLECTION_NAME_LENGTH`]).306 /// * `collection_description`: Wide-character string with collection description307 /// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).308 /// * `token_prefix`: Byte string containing the token prefix to mark a collection309 /// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).310 /// * `mode`: Type of items stored in the collection and type dependent data.311 // returns collection ID312 #[weight = <SelfWeightOf<T>>::create_collection()]313 #[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]314 pub fn create_collection(315 origin,316 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,317 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,318 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,319 mode: CollectionMode320 ) -> DispatchResult {321 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {322 name: collection_name,323 description: collection_description,324 token_prefix,325 mode,326 ..Default::default()327 };328 Self::create_collection_ex(origin, data)329 }330331 /// Create a collection with explicit parameters.332 ///333 /// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.334 ///335 /// # Permissions336 ///337 /// * Anyone - becomes the owner of the new collection.338 ///339 /// # Arguments340 ///341 /// * `data`: Explicit data of a collection used for its creation.342 #[weight = <SelfWeightOf<T>>::create_collection()]343 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {344 let sender = ensure_signed(origin)?;345346 // =========347 let sender = T::CrossAccountId::from_sub(sender);348 let _id = T::CollectionDispatch::create(sender.clone(), sender, data, Default::default())?;349350 Ok(())351 }352353 /// Destroy a collection if no tokens exist within.354 ///355 /// # Permissions356 ///357 /// * Collection owner358 ///359 /// # Arguments360 ///361 /// * `collection_id`: Collection to destroy.362 #[weight = <SelfWeightOf<T>>::destroy_collection()]363 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {364 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);365366 Self::destroy_collection_internal(sender, collection_id)367 }368369 /// Add an address to allow list.370 ///371 /// # Permissions372 ///373 /// * Collection owner374 /// * Collection admin375 ///376 /// # Arguments377 ///378 /// * `collection_id`: ID of the modified collection.379 /// * `address`: ID of the address to be added to the allowlist.380 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]381 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{382383 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);384 let collection = <CollectionHandle<T>>::try_get(collection_id)?;385 collection.check_is_internal()?;386387 <PalletCommon<T>>::toggle_allowlist(388 &collection,389 &sender,390 &address,391 true,392 )?;393394 Self::deposit_event(Event::<T>::AllowListAddressAdded(395 collection_id,396 address397 ));398399 Ok(())400 }401402 /// Remove an address from allow list.403 ///404 /// # Permissions405 ///406 /// * Collection owner407 /// * Collection admin408 ///409 /// # Arguments410 ///411 /// * `collection_id`: ID of the modified collection.412 /// * `address`: ID of the address to be removed from the allowlist.413 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]414 pub fn remove_from_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)?;418 collection.check_is_internal()?;419420 <PalletCommon<T>>::toggle_allowlist(421 &collection,422 &sender,423 &address,424 false,425 )?;426427 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(428 collection_id,429 address430 ));431432 Ok(())433 }434435 /// Change the owner of the collection.436 ///437 /// # Permissions438 ///439 /// * Collection owner440 ///441 /// # Arguments442 ///443 /// * `collection_id`: ID of the modified collection.444 /// * `new_owner`: ID of the account that will become the owner.445 #[weight = <SelfWeightOf<T>>::change_collection_owner()]446 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {447448 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);449450 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;451 target_collection.check_is_internal()?;452 target_collection.check_is_owner(&sender)?;453454 target_collection.owner = new_owner.clone();455 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(456 collection_id,457 new_owner458 ));459460 target_collection.save()461 }462463 /// Add an admin to a collection.464 ///465 /// NFT Collection can be controlled by multiple admin addresses466 /// (some which can also be servers, for example). Admins can issue467 /// and burn NFTs, as well as add and remove other admins,468 /// but cannot change NFT or Collection ownership.469 ///470 /// # Permissions471 ///472 /// * Collection owner473 /// * Collection admin474 ///475 /// # Arguments476 ///477 /// * `collection_id`: ID of the Collection to add an admin for.478 /// * `new_admin`: Address of new admin to add.479 #[weight = <SelfWeightOf<T>>::add_collection_admin()]480 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {481 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);482 let collection = <CollectionHandle<T>>::try_get(collection_id)?;483 collection.check_is_internal()?;484485 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(486 collection_id,487 new_admin_id.clone()488 ));489490 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)491 }492493 /// Remove admin of a collection.494 ///495 /// An admin address can remove itself. List of admins may become empty,496 /// in which case only Collection Owner will be able to add an Admin.497 ///498 /// # Permissions499 ///500 /// * Collection owner501 /// * Collection admin502 ///503 /// # Arguments504 ///505 /// * `collection_id`: ID of the collection to remove the admin for.506 /// * `account_id`: Address of the admin to remove.507 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]508 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {509 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);510 let collection = <CollectionHandle<T>>::try_get(collection_id)?;511 collection.check_is_internal()?;512513 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(514 collection_id,515 account_id.clone()516 ));517518 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)519 }520521 /// Set (invite) a new collection sponsor.522 ///523 /// If successful, confirmation from the sponsor-to-be will be pending.524 ///525 /// # Permissions526 ///527 /// * Collection owner528 /// * Collection admin529 ///530 /// # Arguments531 ///532 /// * `collection_id`: ID of the modified collection.533 /// * `new_sponsor`: ID of the account of the sponsor-to-be.534 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]535 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {536 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);537538 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;539 target_collection.check_is_owner_or_admin(&sender)?;540 target_collection.check_is_internal()?;541542 target_collection.set_sponsor(new_sponsor.clone())?;543544 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(545 collection_id,546 new_sponsor547 ));548549 target_collection.save()550 }551552 /// Confirm own sponsorship of a collection, becoming the sponsor.553 ///554 /// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].555 /// Sponsor can pay the fees of a transaction instead of the sender,556 /// but only within specified limits.557 ///558 /// # Permissions559 ///560 /// * Sponsor-to-be561 ///562 /// # Arguments563 ///564 /// * `collection_id`: ID of the collection with the pending sponsor.565 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]566 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {567 let sender = ensure_signed(origin)?;568569 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;570 target_collection.check_is_internal()?;571 ensure!(572 target_collection.confirm_sponsorship(&sender)?,573 Error::<T>::ConfirmUnsetSponsorFail574 );575576 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(577 collection_id,578 sender579 ));580581 target_collection.save()582 }583584 /// Remove a collection's a sponsor, making everyone pay for their own transactions.585 ///586 /// # Permissions587 ///588 /// * Collection owner589 ///590 /// # Arguments591 ///592 /// * `collection_id`: ID of the collection with the sponsor to remove.593 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]594 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {595 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);596597 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;598 target_collection.check_is_internal()?;599 target_collection.check_is_owner(&sender)?;600601 target_collection.sponsorship = SponsorshipState::Disabled;602603 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(604 collection_id605 ));606 target_collection.save()607 }608609 /// Mint an item within a collection.610 ///611 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].612 ///613 /// # Permissions614 ///615 /// * Collection owner616 /// * Collection admin617 /// * Anyone if618 /// * Allow List is enabled, and619 /// * Address is added to allow list, and620 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])621 ///622 /// # Arguments623 ///624 /// * `collection_id`: ID of the collection to which an item would belong.625 /// * `owner`: Address of the initial owner of the item.626 /// * `data`: Token data describing the item to store on chain.627 #[weight = T::CommonWeightInfo::create_item()]628 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630 let budget = budget::Value::new(NESTING_BUDGET);631632 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))633 }634635 /// Create multiple items within a collection.636 ///637 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].638 ///639 /// # Permissions640 ///641 /// * Collection owner642 /// * Collection admin643 /// * Anyone if644 /// * Allow List is enabled, and645 /// * Address is added to the allow list, and646 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])647 ///648 /// # Arguments649 ///650 /// * `collection_id`: ID of the collection to which the tokens would belong.651 /// * `owner`: Address of the initial owner of the tokens.652 /// * `items_data`: Vector of data describing each item to be created.653 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]654 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {655 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);656 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);657 let budget = budget::Value::new(NESTING_BUDGET);658659 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))660 }661662 /// Add or change collection properties.663 ///664 /// # Permissions665 ///666 /// * Collection owner667 /// * Collection admin668 ///669 /// # Arguments670 ///671 /// * `collection_id`: ID of the modified collection.672 /// * `properties`: Vector of key-value pairs stored as the collection's metadata.673 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.674 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]675 pub fn set_collection_properties(676 origin,677 collection_id: CollectionId,678 properties: Vec<Property>679 ) -> DispatchResultWithPostInfo {680 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);681682 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);683684 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))685 }686687 /// Delete specified collection properties.688 ///689 /// # Permissions690 ///691 /// * Collection Owner692 /// * Collection Admin693 ///694 /// # Arguments695 ///696 /// * `collection_id`: ID of the modified collection.697 /// * `property_keys`: Vector of keys of the properties to be deleted.698 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.699 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]700 pub fn delete_collection_properties(701 origin,702 collection_id: CollectionId,703 property_keys: Vec<PropertyKey>,704 ) -> DispatchResultWithPostInfo {705 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);706707 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);708709 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))710 }711712 /// Add or change token properties according to collection's permissions.713 /// Currently properties only work with NFTs.714 ///715 /// # Permissions716 ///717 /// * Depends on collection's token property permissions and specified property mutability:718 /// * Collection owner719 /// * Collection admin720 /// * Token owner721 ///722 /// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].723 ///724 /// # Arguments725 ///726 /// * `collection_id: ID of the collection to which the token belongs.727 /// * `token_id`: ID of the modified token.728 /// * `properties`: Vector of key-value pairs stored as the token's metadata.729 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.730 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]731 pub fn set_token_properties(732 origin,733 collection_id: CollectionId,734 token_id: TokenId,735 properties: Vec<Property>736 ) -> DispatchResultWithPostInfo {737 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);738739 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);740 let budget = budget::Value::new(NESTING_BUDGET);741742 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))743 }744745 /// Delete specified token properties. Currently properties only work with NFTs.746 ///747 /// # Permissions748 ///749 /// * Depends on collection's token property permissions and specified property mutability:750 /// * Collection owner751 /// * Collection admin752 /// * Token owner753 ///754 /// # Arguments755 ///756 /// * `collection_id`: ID of the collection to which the token belongs.757 /// * `token_id`: ID of the modified token.758 /// * `property_keys`: Vector of keys of the properties to be deleted.759 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.760 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]761 pub fn delete_token_properties(762 origin,763 collection_id: CollectionId,764 token_id: TokenId,765 property_keys: Vec<PropertyKey>766 ) -> DispatchResultWithPostInfo {767 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);768769 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);770 let budget = budget::Value::new(NESTING_BUDGET);771772 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))773 }774775 /// Add or change token property permissions of a collection.776 ///777 /// Without a permission for a particular key, a property with that key778 /// cannot be created in a token.779 ///780 /// # Permissions781 ///782 /// * Collection owner783 /// * Collection admin784 ///785 /// # Arguments786 ///787 /// * `collection_id`: ID of the modified collection.788 /// * `property_permissions`: Vector of permissions for property keys.789 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.790 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]791 pub fn set_token_property_permissions(792 origin,793 collection_id: CollectionId,794 property_permissions: Vec<PropertyKeyPermission>,795 ) -> DispatchResultWithPostInfo {796 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);797798 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);799800 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))801 }802803 /// Create multiple items within a collection with explicitly specified initial parameters.804 ///805 /// # Permissions806 ///807 /// * Collection owner808 /// * Collection admin809 /// * Anyone if810 /// * Allow List is enabled, and811 /// * Address is added to allow list, and812 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])813 ///814 /// # Arguments815 ///816 /// * `collection_id`: ID of the collection to which the tokens would belong.817 /// * `data`: Explicit item creation data.818 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]819 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {820 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);821 let budget = budget::Value::new(NESTING_BUDGET);822823 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))824 }825826 /// Completely allow or disallow transfers for a particular collection.827 ///828 /// # Permissions829 ///830 /// * Collection owner831 ///832 /// # Arguments833 ///834 /// * `collection_id`: ID of the collection.835 /// * `value`: New value of the flag, are transfers allowed?836 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]837 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {838 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);839 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;840 target_collection.check_is_internal()?;841 target_collection.check_is_owner(&sender)?;842843 // =========844845 target_collection.limits.transfers_enabled = Some(value);846 target_collection.save()847 }848849 /// Destroy an item.850 ///851 /// # Permissions852 ///853 /// * Collection owner854 /// * Collection admin855 /// * Current item owner856 ///857 /// # Arguments858 ///859 /// * `collection_id`: ID of the collection to which the item belongs.860 /// * `item_id`: ID of item to burn.861 /// * `value`: Number of pieces of the item to destroy.862 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.863 /// * Fungible Mode: The desired number of pieces to burn.864 /// * Re-Fungible Mode: The desired number of pieces to burn.865 #[weight = T::CommonWeightInfo::burn_item()]866 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {867 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);868869 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;870 if value == 1 {871 <NftTransferBasket<T>>::remove(collection_id, item_id);872 <NftApproveBasket<T>>::remove(collection_id, item_id);873 }874 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?875 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());876 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));877 Ok(post_info)878 }879880 /// Destroy a token on behalf of the owner as a non-owner account.881 ///882 /// See also: [`approve`][`Pallet::approve`].883 ///884 /// After this method executes, one approval is removed from the total so that885 /// the approved address will not be able to transfer this item again from this owner.886 ///887 /// # Permissions888 ///889 /// * Collection owner890 /// * Collection admin891 /// * Current token owner892 /// * Address approved by current item owner893 ///894 /// # Arguments895 ///896 /// * `from`: The owner of the burning item.897 /// * `collection_id`: ID of the collection to which the item belongs.898 /// * `item_id`: ID of item to burn.899 /// * `value`: Number of pieces to burn.900 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.901 /// * Fungible Mode: The desired number of pieces to burn.902 /// * Re-Fungible Mode: The desired number of pieces to burn.903 #[weight = T::CommonWeightInfo::burn_from()]904 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {905 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);906 let budget = budget::Value::new(NESTING_BUDGET);907908 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))909 }910911 /// Change ownership of the token.912 ///913 /// # Permissions914 ///915 /// * Collection owner916 /// * Collection admin917 /// * Current token owner918 ///919 /// # Arguments920 ///921 /// * `recipient`: Address of token recipient.922 /// * `collection_id`: ID of the collection the item belongs to.923 /// * `item_id`: ID of the item.924 /// * Non-Fungible Mode: Required.925 /// * Fungible Mode: Ignored.926 /// * Re-Fungible Mode: Required.927 ///928 /// * `value`: Amount to transfer.929 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.930 /// * Fungible Mode: The desired number of pieces to transfer.931 /// * Re-Fungible Mode: The desired number of pieces to transfer.932 #[weight = T::CommonWeightInfo::transfer()]933 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {934 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);935 let budget = budget::Value::new(NESTING_BUDGET);936937 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))938 }939940 /// Allow a non-permissioned address to transfer or burn an item.941 ///942 /// # Permissions943 ///944 /// * Collection owner945 /// * Collection admin946 /// * Current item owner947 ///948 /// # Arguments949 ///950 /// * `spender`: Account to be approved to make specific transactions on non-owned tokens.951 /// * `collection_id`: ID of the collection the item belongs to.952 /// * `item_id`: ID of the item transactions on which are now approved.953 /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).954 /// Set to 0 to revoke the approval.955 #[weight = T::CommonWeightInfo::approve()]956 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {957 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);958959 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))960 }961962 /// Change ownership of an item on behalf of the owner as a non-owner account.963 ///964 /// See the [`approve`][`Pallet::approve`] method for additional information.965 ///966 /// After this method executes, one approval is removed from the total so that967 /// the approved address will not be able to transfer this item again from this owner.968 ///969 /// # Permissions970 ///971 /// * Collection owner972 /// * Collection admin973 /// * Current item owner974 /// * Address approved by current item owner975 ///976 /// # Arguments977 ///978 /// * `from`: Address that currently owns the token.979 /// * `recipient`: Address of the new token-owner-to-be.980 /// * `collection_id`: ID of the collection the item.981 /// * `item_id`: ID of the item to be transferred.982 /// * `value`: Amount to transfer.983 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.984 /// * Fungible Mode: The desired number of pieces to transfer.985 /// * Re-Fungible Mode: The desired number of pieces to transfer.986 #[weight = T::CommonWeightInfo::transfer_from()]987 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {988 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);989 let budget = budget::Value::new(NESTING_BUDGET);990991 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))992 }993994 /// Set specific limits of a collection. Empty, or None fields mean chain default.995 ///996 /// # Permissions997 ///998 /// * Collection owner999 /// * Collection admin1000 ///1001 /// # Arguments1002 ///1003 /// * `collection_id`: ID of the modified collection.1004 /// * `new_limit`: New limits of the collection. Fields that are not set (None)1005 /// will not overwrite the old ones.1006 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1007 pub fn set_collection_limits(1008 origin,1009 collection_id: CollectionId,1010 new_limit: CollectionLimits,1011 ) -> DispatchResult {1012 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1014 target_collection.check_is_internal()?;1015 target_collection.check_is_owner_or_admin(&sender)?;1016 let old_limit = &target_collection.limits;10171018 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10191020 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1021 collection_id1022 ));10231024 target_collection.save()1025 }10261027 /// Set specific permissions of a collection. Empty, or None fields mean chain default.1028 ///1029 /// # Permissions1030 ///1031 /// * Collection owner1032 /// * Collection admin1033 ///1034 /// # Arguments1035 ///1036 /// * `collection_id`: ID of the modified collection.1037 /// * `new_permission`: New permissions of the collection. Fields that are not set (None)1038 /// will not overwrite the old ones.1039 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1040 pub fn set_collection_permissions(1041 origin,1042 collection_id: CollectionId,1043 new_permission: CollectionPermissions,1044 ) -> DispatchResult {1045 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1046 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1047 target_collection.check_is_internal()?;1048 target_collection.check_is_owner_or_admin(&sender)?;1049 let old_limit = &target_collection.permissions;10501051 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10521053 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1054 collection_id1055 ));10561057 target_collection.save()1058 }10591060 /// Re-partition a refungible token, while owning all of its parts/pieces.1061 ///1062 /// # Permissions1063 ///1064 /// * Token owner (must own every part)1065 ///1066 /// # Arguments1067 ///1068 /// * `collection_id`: ID of the collection the RFT belongs to.1069 /// * `token_id`: ID of the RFT.1070 /// * `amount`: New number of parts/pieces into which the token shall be partitioned.1071 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]1072 pub fn repartition(1073 origin,1074 collection_id: CollectionId,1075 token_id: TokenId,1076 amount: u128,1077 ) -> DispatchResultWithPostInfo {1078 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1079 dispatch_tx::<T, _>(collection_id, |d| {1080 if let Some(refungible_extensions) = d.refungible_extensions() {1081 refungible_extensions.repartition(&sender, token_id, amount)1082 } else {1083 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1084 }1085 })1086 }1087 }1088}10891090impl<T: Config> Pallet<T> {1091 /// Force set `sponsor` for `collection`.1092 ///1093 /// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation1094 /// from the `sponsor` is not required.1095 ///1096 /// # Arguments1097 ///1098 /// * `sponsor`: ID of the account of the sponsor-to-be.1099 /// * `collection_id`: ID of the modified collection.1100 pub fn force_set_sponsor(sponsor: T::AccountId, collection_id: CollectionId) -> DispatchResult {1101 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1102 target_collection.check_is_internal()?;1103 target_collection.set_sponsor(sponsor.clone())?;11041105 Self::deposit_event(Event::<T>::CollectionSponsorSet(1106 collection_id,1107 sponsor.clone(),1108 ));11091110 ensure!(1111 target_collection.confirm_sponsorship(&sponsor)?,1112 Error::<T>::ConfirmUnsetSponsorFail1113 );11141115 Self::deposit_event(Event::<T>::SponsorshipConfirmed(collection_id, sponsor));11161117 target_collection.save()1118 }11191120 /// Force remove `sponsor` for `collection`.1121 ///1122 /// Differs from `remove_sponsor` in that1123 /// it doesn't require consent from the `owner` of the collection.1124 ///1125 /// # Arguments1126 ///1127 /// * `collection_id`: ID of the modified collection.1128 pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1129 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1130 target_collection.check_is_internal()?;1131 target_collection.sponsorship = SponsorshipState::Disabled;11321133 Self::deposit_event(Event::<T>::CollectionSponsorRemoved(collection_id));11341135 target_collection.save()1136 }11371138 #[inline(always)]1139 pub(crate) fn destroy_collection_internal(1140 sender: T::CrossAccountId,1141 collection_id: CollectionId,1142 ) -> DispatchResult {1143 let collection = <CollectionHandle<T>>::try_get(collection_id)?;1144 collection.check_is_internal()?;11451146 T::CollectionDispatch::destroy(sender, collection)?;11471148 // TODO: basket cleanup should be moved elsewhere1149 // Maybe runtime dispatch.rs should perform it?11501151 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1152 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1153 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);11541155 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1156 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1157 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);11581159 Ok(())1160 }1161}primitives/data-structs/CHANGELOG.mddiffbeforeafterboth--- a/primitives/data-structs/CHANGELOG.md
+++ b/primitives/data-structs/CHANGELOG.md
@@ -3,6 +3,7 @@
All notable changes to this project will be documented in this file.
<!-- bureaucrate goes here -->
+
## [v0.2.2] 2022-08-16
### Other changes
@@ -28,12 +29,19 @@
multiple users into `RefungibleMultipleItems` call.
## [v0.2.0] - 2022-08-01
+
### Deprecated
+
- `CreateReFungibleData::const_data`
## [v0.1.2] - 2022-07-25
+
### Added
+
- Type aliases `CollectionName`, `CollectionDescription`, `CollectionTokenPrefix`
+
## [v0.1.1] - 2022-07-22
+
### Added
-- Аields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
\ No newline at end of file
+
+- Аields with properties to `CreateReFungibleData` and `CreateRefungibleExData`.
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -609,6 +609,24 @@
}
impl CollectionLimits {
+ pub fn with_default_limits(collection_type: CollectionMode) -> Self {
+ CollectionLimits {
+ account_token_ownership_limit: Some(ACCOUNT_TOKEN_OWNERSHIP_LIMIT),
+ sponsored_data_size: Some(CUSTOM_DATA_LIMIT),
+ sponsored_data_rate_limit: Some(SponsoringRateLimit::SponsoringDisabled),
+ token_limit: Some(COLLECTION_TOKEN_LIMIT),
+ sponsor_transfer_timeout: match collection_type {
+ CollectionMode::NFT => Some(NFT_SPONSOR_TRANSFER_TIMEOUT),
+ CollectionMode::ReFungible => Some(REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),
+ CollectionMode::Fungible(_) => Some(FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT),
+ },
+ sponsor_approve_timeout: Some(SPONSOR_APPROVE_TIMEOUT),
+ owner_can_transfer: Some(false),
+ owner_can_destroy: Some(true),
+ transfers_enabled: Some(true),
+ }
+ }
+
/// Get effective value for [`account_token_ownership_limit`](self.account_token_ownership_limit).
pub fn account_token_ownership_limit(&self) -> u32 {
self.account_token_ownership_limit
tests/package.jsondiffbeforeafterboth--- a/tests/package.json
+++ b/tests/package.json
@@ -102,6 +102,7 @@
"testXcmTransferStatemine": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferStatemine.test.ts statemineId=1000 uniqueId=5000",
"testXcmTransferMoonbeam": "mocha --timeout 9999999 -r ts-node/register ./**/xcm/xcmTransferMoonbeam.test.ts",
"benchMintingFee": "ts-node src/benchmarks/mintFee/benchmark.ts",
+ "testApiConsts": "mocha --timeout 9999999 -r ts-node/register ./**/apiConsts.test.ts",
"load": "mocha --timeout 9999999 -r ts-node/register './**/*.load.ts'",
"loadTransfer": "ts-node src/transfer.nload.ts",
"polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json",
tests/src/apiConsts.test.tsdiffbeforeafterboth--- /dev/null
+++ b/tests/src/apiConsts.test.ts
@@ -0,0 +1,108 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+import {ApiPromise} from '@polkadot/api';
+import {usingPlaygrounds, itSub, expect} from './util';
+
+
+const MAX_COLLECTION_DESCRIPTION_LENGTH = 256n;
+const MAX_COLLECTION_NAME_LENGTH = 64n;
+const COLLECTION_ADMINS_LIMIT = 5n;
+const MAX_COLLECTION_PROPERTIES_SIZE = 40960n;
+const MAX_TOKEN_PREFIX_LENGTH = 16n;
+const MAX_PROPERTY_KEY_LENGTH = 256n;
+const MAX_PROPERTY_VALUE_LENGTH = 32768n;
+const MAX_PROPERTIES_PER_ITEM = 64n;
+const MAX_TOKEN_PROPERTIES_SIZE = 32768n;
+const NESTING_BUDGET = 5n;
+
+const DEFAULT_COLLETCTION_LIMIT = {
+ accountTokenOwnershipLimit: '1,000,000',
+ sponsoredDataSize: '2,048',
+ sponsoredDataRateLimit: 'SponsoringDisabled',
+ tokenLimit: '4,294,967,295',
+ sponsorTransferTimeout: '5',
+ sponsorApproveTimeout: '5',
+ ownerCanTransfer: false,
+ ownerCanDestroy: true,
+ transfersEnabled: true,
+};
+
+describe('integration test: API UNIQUE consts', () => {
+ let api: ApiPromise;
+
+ before(async () => {
+ await usingPlaygrounds(async (helper) => {
+ api = await helper.getApi();
+ });
+ });
+
+ itSub('DEFAULT_NFT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.nftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('DEFAULT_RFT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.rftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('DEFAULT_FT_COLLECTION_LIMITS', () => {
+ expect(api.consts.unique.ftDefaultCollectionLimits.toHuman()).to.deep.equal(DEFAULT_COLLETCTION_LIMIT);
+ });
+
+ itSub('MAX_COLLECTION_NAME_LENGTH', () => {
+ checkConst(api.consts.unique.maxCollectionNameLength, MAX_COLLECTION_NAME_LENGTH);
+ });
+
+ itSub('MAX_COLLECTION_DESCRIPTION_LENGTH', () => {
+ checkConst(api.consts.unique.maxCollectionDescriptionLength, MAX_COLLECTION_DESCRIPTION_LENGTH);
+ });
+
+ itSub('MAX_COLLECTION_PROPERTIES_SIZE', () => {
+ checkConst(api.consts.unique.maxCollectionPropertiesSize, MAX_COLLECTION_PROPERTIES_SIZE);
+ });
+
+ itSub('MAX_TOKEN_PREFIX_LENGTH', () => {
+ checkConst(api.consts.unique.maxTokenPrefixLength, MAX_TOKEN_PREFIX_LENGTH);
+ });
+
+ itSub('MAX_PROPERTY_KEY_LENGTH', () => {
+ checkConst(api.consts.unique.maxPropertyKeyLength, MAX_PROPERTY_KEY_LENGTH);
+ });
+
+ itSub('MAX_PROPERTY_VALUE_LENGTH', () => {
+ checkConst(api.consts.unique.maxPropertyValueLength, MAX_PROPERTY_VALUE_LENGTH);
+ });
+
+ itSub('MAX_PROPERTIES_PER_ITEM', () => {
+ checkConst(api.consts.unique.maxPropertiesPerItem, MAX_PROPERTIES_PER_ITEM);
+ });
+
+ itSub('NESTING_BUDGET', () => {
+ checkConst(api.consts.unique.nestingBudget, NESTING_BUDGET);
+ });
+
+ itSub('MAX_TOKEN_PROPERTIES_SIZE', () => {
+ checkConst(api.consts.unique.maxTokenPropertiesSize, MAX_TOKEN_PROPERTIES_SIZE);
+ });
+
+ itSub('COLLECTION_ADMINS_LIMIT', () => {
+ checkConst(api.consts.unique.collectionAdminsLimit, COLLECTION_ADMINS_LIMIT);
+ });
+});
+
+function checkConst<T>(constValue: any, expectedValue: T) {
+ expect(constValue.toBigInt()).equal(expectedValue);
+}
tests/src/interfaces/augment-api-consts.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-consts.ts
+++ b/tests/src/interfaces/augment-api-consts.ts
@@ -9,7 +9,7 @@
import type { Option, u128, u16, u32, u64, u8 } from '@polkadot/types-codec';
import type { Codec } from '@polkadot/types-codec/types';
import type { Perbill, Permill, Weight } from '@polkadot/types/interfaces/runtime';
-import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, XcmV1MultiLocation } from '@polkadot/types/lookup';
+import type { FrameSupportPalletId, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion, SpWeightsRuntimeDbWeight, UpDataStructsCollectionLimits, XcmV1MultiLocation } from '@polkadot/types/lookup';
export type __AugmentedConst<ApiType extends ApiTypes> = AugmentedConst<ApiType>;
@@ -231,6 +231,64 @@
**/
[key: string]: Codec;
};
+ unique: {
+ /**
+ * Maximum admins per collection.
+ **/
+ collectionAdminsLimit: u32 & AugmentedConst<ApiType>;
+ /**
+ * Default FT collection limit.
+ **/
+ ftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Maximum length for collection description.
+ **/
+ maxCollectionDescriptionLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum length for collection name.
+ **/
+ maxCollectionNameLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum size for all collection properties.
+ **/
+ maxCollectionPropertiesSize: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum properties that can be assigned to token.
+ **/
+ maxPropertiesPerItem: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal lenght of property key.
+ **/
+ maxPropertyKeyLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal lenght of property value.
+ **/
+ maxPropertyValueLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximal token prefix length.
+ **/
+ maxTokenPrefixLength: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum size for all token properties.
+ **/
+ maxTokenPropertiesSize: u32 & AugmentedConst<ApiType>;
+ /**
+ * Maximum number of levels of depth in the token nesting tree.
+ **/
+ nestingBudget: u32 & AugmentedConst<ApiType>;
+ /**
+ * Default NFT collection limit.
+ **/
+ nftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Default RFT collection limit.
+ **/
+ rftDefaultCollectionLimits: UpDataStructsCollectionLimits & AugmentedConst<ApiType>;
+ /**
+ * Generic const
+ **/
+ [key: string]: Codec;
+ };
vesting: {
/**
* The minimum amount transferred to call `vested_transfer`.