difftreelog
refactor switch to new prefix removal methods
in: master
New methods allows to call `remove_prefix` with limit multiple times in the same block However, we don't use prefix removal limits, so upgrade is straightforward
5 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -958,8 +958,8 @@
<DestroyedCollectionCount<T>>::put(destroyed_collections);
<CollectionById<T>>::remove(collection.id);
<AdminAmount<T>>::remove(collection.id);
- <IsAdmin<T>>::remove_prefix((collection.id,), None);
- <Allowlist<T>>::remove_prefix((collection.id,), None);
+ let _ = <IsAdmin<T>>::clear_prefix((collection.id,), u32::MAX, None);
+ let _ = <Allowlist<T>>::clear_prefix((collection.id,), u32::MAX, None);
<CollectionProperties<T>>::remove(collection.id);
<Pallet<T>>::deposit_event(Event::CollectionDestroyed(collection.id));
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -231,8 +231,8 @@
PalletCommon::destroy_collection(collection.0, sender)?;
<TotalSupply<T>>::remove(id);
- <Balance<T>>::remove_prefix((id,), None);
- <Allowance<T>>::remove_prefix((id,), None);
+ let _ = <Balance<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);
Ok(())
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -429,13 +429,13 @@
PalletCommon::destroy_collection(collection.0, sender)?;
- <TokenData<T>>::remove_prefix((id,), None);
- <TokenChildren<T>>::remove_prefix((id,), None);
- <Owned<T>>::remove_prefix((id,), None);
+ let _ = <TokenData<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <TokenChildren<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <Owned<T>>::clear_prefix((id,), u32::MAX, None);
<TokensMinted<T>>::remove(id);
<TokensBurnt<T>>::remove(id);
- <Allowance<T>>::remove_prefix((id,), None);
- <AccountBalance<T>>::remove_prefix((id,), None);
+ let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <AccountBalance<T>>::clear_prefix((id,), u32::MAX, None);
Ok(())
}
@@ -487,7 +487,7 @@
<TokensBurnt<T>>::insert(collection.id, burnt);
<TokenData<T>>::remove((collection.id, token));
<TokenProperties<T>>::remove((collection.id, token));
- <TokenAuxProperties<T>>::remove_prefix((collection.id, token), None);
+ let _ = <TokenAuxProperties<T>>::clear_prefix((collection.id, token), u32::MAX, None);
let old_spender = <Allowance<T>>::take((collection.id, token));
if let Some(old_spender) = old_spender {
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -398,11 +398,11 @@
<TokensMinted<T>>::remove(id);
<TokensBurnt<T>>::remove(id);
- <TotalSupply<T>>::remove_prefix((id,), None);
- <Balance<T>>::remove_prefix((id,), None);
- <Allowance<T>>::remove_prefix((id,), None);
- <Owned<T>>::remove_prefix((id,), None);
- <AccountBalance<T>>::remove_prefix((id,), None);
+ let _ = <TotalSupply<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <Balance<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <Allowance<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <Owned<T>>::clear_prefix((id,), u32::MAX, None);
+ let _ = <AccountBalance<T>>::clear_prefix((id,), u32::MAX, None);
Ok(())
}
@@ -424,6 +424,7 @@
<TokensBurnt<T>>::insert(collection.id, burnt);
<TokenProperties<T>>::remove((collection.id, token_id));
<TotalSupply<T>>::remove((collection.id, token_id));
+<<<<<<< HEAD
<Balance<T>>::remove_prefix((collection.id, token_id), None);
<Allowance<T>>::remove_prefix((collection.id, token_id), None);
@@ -435,6 +436,11 @@
}
.to_log(collection_id_to_address(collection.id)),
);
+=======
+ let _ = <Balance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);
+ let _ = <Allowance<T>>::clear_prefix((collection.id, token_id), u32::MAX, None);
+ // TODO: ERC721 transfer event
+>>>>>>> 5d9665e0... refactor: switch to new prefix removal methods
Ok(())
}
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 transactional,82 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},83 BoundedVec,84};85use scale_info::TypeInfo;86use frame_system::{self as system, ensure_signed};87use sp_runtime::{sp_std::prelude::Vec};88use up_data_structs::{89 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH,90 CreateItemData, CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId,91 SponsorshipState, CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,92 PropertyKeyPermission,93};94use pallet_evm::account::CrossAccountId;95use pallet_common::{96 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,97 dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo,98};99pub mod eth;100101#[cfg(feature = "runtime-benchmarks")]102mod benchmarking;103pub mod weights;104use weights::WeightInfo;105106/// Maximum number of levels of depth in the token nesting tree.107pub const NESTING_BUDGET: u32 = 5;108109decl_error! {110 /// Errors for the common Unique transactions.111 pub enum Error for Module<T: Config> {112 /// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].113 CollectionDecimalPointLimitExceeded,114 /// This address is not set as sponsor, use setCollectionSponsor first.115 ConfirmUnsetSponsorFail,116 /// Length of items properties must be greater than 0.117 EmptyArgument,118 /// Repertition is only supported by refungible collection.119 RepartitionCalledOnNonRefungibleCollection,120 }121}122123/// Configuration trait of this pallet.124pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {125 /// Overarching event type.126 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;127128 /// Weight information for extrinsics in this pallet.129 type WeightInfo: WeightInfo;130131 /// Weight information for common pallet operations.132 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;133134 /// Weight info information for extra refungible pallet operations.135 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;136}137138decl_event! {139 pub enum Event<T>140 where141 <T as frame_system::Config>::AccountId,142 <T as pallet_evm::account::Config>::CrossAccountId,143 {144 /// Collection sponsor was removed145 ///146 /// # Arguments147 /// * collection_id: ID of the affected collection.148 CollectionSponsorRemoved(CollectionId),149150 /// Collection admin was added151 ///152 /// # Arguments153 /// * collection_id: ID of the affected collection.154 /// * admin: Admin address.155 CollectionAdminAdded(CollectionId, CrossAccountId),156157 /// Collection owned was changed158 ///159 /// # Arguments160 /// * collection_id: ID of the affected collection.161 /// * owner: New owner address.162 CollectionOwnedChanged(CollectionId, AccountId),163164 /// Collection sponsor was set165 ///166 /// # Arguments167 /// * collection_id: ID of the affected collection.168 /// * owner: New sponsor address.169 CollectionSponsorSet(CollectionId, AccountId),170171 /// New sponsor was confirm172 ///173 /// # Arguments174 /// * collection_id: ID of the affected collection.175 /// * sponsor: New sponsor address.176 SponsorshipConfirmed(CollectionId, AccountId),177178 /// Collection admin was removed179 ///180 /// # Arguments181 /// * collection_id: ID of the affected collection.182 /// * admin: Removed admin address.183 CollectionAdminRemoved(CollectionId, CrossAccountId),184185 /// Address was removed from the allow list186 ///187 /// # Arguments188 /// * collection_id: ID of the affected collection.189 /// * user: Address of the removed account.190 AllowListAddressRemoved(CollectionId, CrossAccountId),191192 /// Address was added to the allow list193 ///194 /// # Arguments195 /// * collection_id: ID of the affected collection.196 /// * user: Address of the added account.197 AllowListAddressAdded(CollectionId, CrossAccountId),198199 /// Collection limits were set200 ///201 /// # Arguments202 /// * collection_id: ID of the affected collection.203 CollectionLimitSet(CollectionId),204205 /// Collection permissions were set206 ///207 /// # Arguments208 /// * collection_id: ID of the affected collection.209 CollectionPermissionSet(CollectionId),210 }211}212213type SelfWeightOf<T> = <T as Config>::WeightInfo;214215// # Used definitions216//217// ## User control levels218//219// chain-controlled - key is uncontrolled by user220// i.e autoincrementing index221// can use non-cryptographic hash222// real - key is controlled by user223// but it is hard to generate enough colliding values, i.e owner of signed txs224// can use non-cryptographic hash225// controlled - key is completly controlled by users226// i.e maps with mutable keys227// should use cryptographic hash228//229// ## User control level downgrade reasons230//231// ?1 - chain-controlled -> controlled232// collections/tokens can be destroyed, resulting in massive holes233// ?2 - chain-controlled -> controlled234// same as ?1, but can be only added, resulting in easier exploitation235// ?3 - real -> controlled236// no confirmation required, so addresses can be easily generated237decl_storage! {238 trait Store for Module<T: Config> as Unique {239240 //#region Private members241 /// Used for migrations242 ChainVersion: u64;243 //#endregion244245 //#region Tokens transfer sponosoring rate limit baskets246 /// (Collection id (controlled?2), who created (real))247 /// TODO: Off chain worker should remove from this map when collection gets removed248 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;249 /// Collection id (controlled?2), token id (controlled?2)250 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;251 /// Collection id (controlled?2), owning user (real)252 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;253 /// Collection id (controlled?2), token id (controlled?2)254 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>;255 //#endregion256257 /// Variable metadata sponsoring258 /// Collection id (controlled?2), token id (controlled?2)259 #[deprecated]260 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;261 /// Last sponsoring of token property setting // todo:doc rephrase this and the following262 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;263264 /// Last sponsoring of NFT approval in a collection265 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;266 /// Last sponsoring of fungible tokens approval in a collection267 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;268 /// Last sponsoring of RFT approval in a collection269 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>;270 }271}272273decl_module! {274 /// Type alias to Pallet, to be used by construct_runtime.275 pub struct Module<T: Config> for enum Call276 where277 origin: T::Origin278 {279 type Error = Error<T>;280281 fn deposit_event() = default;282283 fn on_initialize(_now: T::BlockNumber) -> Weight {284 0285 }286287 fn on_runtime_upgrade() -> Weight {288 0289 }290291 /// Create a collection of tokens.292 ///293 /// Each Token may have multiple properties encoded as an array of bytes294 /// of certain length. The initial owner of the collection is set295 /// to the address that signed the transaction and can be changed later.296 ///297 /// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.298 ///299 /// # Permissions300 ///301 /// * Anyone - becomes the owner of the new collection.302 ///303 /// # Arguments304 ///305 /// * `collection_name`: Wide-character string with collection name306 /// (limit [`MAX_COLLECTION_NAME_LENGTH`]).307 /// * `collection_description`: Wide-character string with collection description308 /// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).309 /// * `token_prefix`: Byte string containing the token prefix to mark a collection310 /// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).311 /// * `mode`: Type of items stored in the collection and type dependent data.312 // returns collection ID313 #[weight = <SelfWeightOf<T>>::create_collection()]314 #[transactional]315 #[deprecated(note = "`create_collection_ex` is more up-to-date and advanced, prefer it instead")]316 pub fn create_collection(317 origin,318 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,319 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,320 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,321 mode: CollectionMode322 ) -> DispatchResult {323 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {324 name: collection_name,325 description: collection_description,326 token_prefix,327 mode,328 ..Default::default()329 };330 Self::create_collection_ex(origin, data)331 }332333 /// Create a collection with explicit parameters.334 ///335 /// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.336 ///337 /// # Permissions338 ///339 /// * Anyone - becomes the owner of the new collection.340 ///341 /// # Arguments342 ///343 /// * `data`: Explicit data of a collection used for its creation.344 #[weight = <SelfWeightOf<T>>::create_collection()]345 #[transactional]346 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {347 let sender = ensure_signed(origin)?;348349 // =========350351 let _id = T::CollectionDispatch::create(T::CrossAccountId::from_sub(sender), data)?;352353 Ok(())354 }355356 /// Destroy a collection if no tokens exist within.357 ///358 /// # Permissions359 ///360 /// * Collection owner361 ///362 /// # Arguments363 ///364 /// * `collection_id`: Collection to destroy.365 #[weight = <SelfWeightOf<T>>::destroy_collection()]366 #[transactional]367 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {368 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);369 let collection = <CollectionHandle<T>>::try_get(collection_id)?;370 collection.check_is_internal()?;371372 // =========373374 T::CollectionDispatch::destroy(sender, collection)?;375376 <NftTransferBasket<T>>::remove_prefix(collection_id, None);377 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);378 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);379380 <NftApproveBasket<T>>::remove_prefix(collection_id, None);381 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);382 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);383384 Ok(())385 }386387 /// Add an address to allow list.388 ///389 /// # Permissions390 ///391 /// * Collection owner392 /// * Collection admin393 ///394 /// # Arguments395 ///396 /// * `collection_id`: ID of the modified collection.397 /// * `address`: ID of the address to be added to the allowlist.398 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]399 #[transactional]400 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{401402 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);403 let collection = <CollectionHandle<T>>::try_get(collection_id)?;404 collection.check_is_internal()?;405406 <PalletCommon<T>>::toggle_allowlist(407 &collection,408 &sender,409 &address,410 true,411 )?;412413 Self::deposit_event(Event::<T>::AllowListAddressAdded(414 collection_id,415 address416 ));417418 Ok(())419 }420421 /// Remove an address from allow list.422 ///423 /// # Permissions424 ///425 /// * Collection owner426 /// * Collection admin427 ///428 /// # Arguments429 ///430 /// * `collection_id`: ID of the modified collection.431 /// * `address`: ID of the address to be removed from the allowlist.432 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]433 #[transactional]434 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{435436 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);437 let collection = <CollectionHandle<T>>::try_get(collection_id)?;438 collection.check_is_internal()?;439440 <PalletCommon<T>>::toggle_allowlist(441 &collection,442 &sender,443 &address,444 false,445 )?;446447 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(448 collection_id,449 address450 ));451452 Ok(())453 }454455 /// Change the owner of the collection.456 ///457 /// # Permissions458 ///459 /// * Collection owner460 ///461 /// # Arguments462 ///463 /// * `collection_id`: ID of the modified collection.464 /// * `new_owner`: ID of the account that will become the owner.465 #[weight = <SelfWeightOf<T>>::change_collection_owner()]466 #[transactional]467 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {468469 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);470471 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;472 target_collection.check_is_internal()?;473 target_collection.check_is_owner(&sender)?;474475 target_collection.owner = new_owner.clone();476 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(477 collection_id,478 new_owner479 ));480481 target_collection.save()482 }483484 /// Add an admin to a collection.485 ///486 /// NFT Collection can be controlled by multiple admin addresses487 /// (some which can also be servers, for example). Admins can issue488 /// and burn NFTs, as well as add and remove other admins,489 /// but cannot change NFT or Collection ownership.490 ///491 /// # Permissions492 ///493 /// * Collection owner494 /// * Collection admin495 ///496 /// # Arguments497 ///498 /// * `collection_id`: ID of the Collection to add an admin for.499 /// * `new_admin`: Address of new admin to add.500 #[weight = <SelfWeightOf<T>>::add_collection_admin()]501 #[transactional]502 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {503 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);504 let collection = <CollectionHandle<T>>::try_get(collection_id)?;505 collection.check_is_internal()?;506507 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(508 collection_id,509 new_admin.clone()510 ));511512 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)513 }514515 /// Remove admin of a collection.516 ///517 /// An admin address can remove itself. List of admins may become empty,518 /// in which case only Collection Owner will be able to add an Admin.519 ///520 /// # Permissions521 ///522 /// * Collection owner523 /// * Collection admin524 ///525 /// # Arguments526 ///527 /// * `collection_id`: ID of the collection to remove the admin for.528 /// * `account_id`: Address of the admin to remove.529 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]530 #[transactional]531 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {532 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);533 let collection = <CollectionHandle<T>>::try_get(collection_id)?;534 collection.check_is_internal()?;535536 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(537 collection_id,538 account_id.clone()539 ));540541 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)542 }543544 /// Set (invite) a new collection sponsor.545 ///546 /// If successful, confirmation from the sponsor-to-be will be pending.547 ///548 /// # Permissions549 ///550 /// * Collection owner551 /// * Collection admin552 ///553 /// # Arguments554 ///555 /// * `collection_id`: ID of the modified collection.556 /// * `new_sponsor`: ID of the account of the sponsor-to-be.557 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]558 #[transactional]559 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {560 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);561562 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;563 target_collection.check_is_owner_or_admin(&sender)?;564 target_collection.check_is_internal()?;565566 target_collection.set_sponsor(new_sponsor.clone())?;567568 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(569 collection_id,570 new_sponsor571 ));572573 target_collection.save()574 }575576 /// Confirm own sponsorship of a collection, becoming the sponsor.577 ///578 /// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].579 /// Sponsor can pay the fees of a transaction instead of the sender,580 /// but only within specified limits.581 ///582 /// # Permissions583 ///584 /// * Sponsor-to-be585 ///586 /// # Arguments587 ///588 /// * `collection_id`: ID of the collection with the pending sponsor.589 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]590 #[transactional]591 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {592 let sender = ensure_signed(origin)?;593594 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;595 target_collection.check_is_internal()?;596 ensure!(597 target_collection.confirm_sponsorship(&sender)?,598 Error::<T>::ConfirmUnsetSponsorFail599 );600601 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(602 collection_id,603 sender604 ));605606 target_collection.save()607 }608609 /// Remove a collection's a sponsor, making everyone pay for their own transactions.610 ///611 /// # Permissions612 ///613 /// * Collection owner614 ///615 /// # Arguments616 ///617 /// * `collection_id`: ID of the collection with the sponsor to remove.618 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]619 #[transactional]620 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {621 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);622623 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;624 target_collection.check_is_internal()?;625 target_collection.check_is_owner(&sender)?;626627 target_collection.sponsorship = SponsorshipState::Disabled;628629 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(630 collection_id631 ));632 target_collection.save()633 }634635 /// Mint an item 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 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 an item would belong.651 /// * `owner`: Address of the initial owner of the item.652 /// * `data`: Token data describing the item to store on chain.653 #[weight = T::CommonWeightInfo::create_item()]654 #[transactional]655 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {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_item(sender, owner, data, &budget))660 }661662 /// Create multiple items within a collection.663 ///664 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].665 ///666 /// # Permissions667 ///668 /// * Collection owner669 /// * Collection admin670 /// * Anyone if671 /// * Allow List is enabled, and672 /// * Address is added to the allow list, and673 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])674 ///675 /// # Arguments676 ///677 /// * `collection_id`: ID of the collection to which the tokens would belong.678 /// * `owner`: Address of the initial owner of the tokens.679 /// * `items_data`: Vector of data describing each item to be created.680 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]681 #[transactional]682 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {683 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);684 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);685 let budget = budget::Value::new(NESTING_BUDGET);686687 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))688 }689690 /// Add or change collection properties.691 ///692 /// # Permissions693 ///694 /// * Collection owner695 /// * Collection admin696 ///697 /// # Arguments698 ///699 /// * `collection_id`: ID of the modified collection.700 /// * `properties`: Vector of key-value pairs stored as the collection's metadata.701 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.702 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]703 #[transactional]704 pub fn set_collection_properties(705 origin,706 collection_id: CollectionId,707 properties: Vec<Property>708 ) -> DispatchResultWithPostInfo {709 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);710711 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);712713 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))714 }715716 /// Delete specified collection properties.717 ///718 /// # Permissions719 ///720 /// * Collection Owner721 /// * Collection Admin722 ///723 /// # Arguments724 ///725 /// * `collection_id`: ID of the modified collection.726 /// * `property_keys`: Vector of keys of the properties to be deleted.727 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.728 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]729 #[transactional]730 pub fn delete_collection_properties(731 origin,732 collection_id: CollectionId,733 property_keys: Vec<PropertyKey>,734 ) -> DispatchResultWithPostInfo {735 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);736737 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);738739 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))740 }741742 /// Add or change token properties according to collection's permissions.743 /// Currently properties only work with NFTs.744 ///745 /// # Permissions746 ///747 /// * Depends on collection's token property permissions and specified property mutability:748 /// * Collection owner749 /// * Collection admin750 /// * Token owner751 ///752 /// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].753 ///754 /// # Arguments755 ///756 /// * `collection_id: ID of the collection to which the token belongs.757 /// * `token_id`: ID of the modified token.758 /// * `properties`: Vector of key-value pairs stored as the token's metadata.759 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.760 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]761 #[transactional]762 pub fn set_token_properties(763 origin,764 collection_id: CollectionId,765 token_id: TokenId,766 properties: Vec<Property>767 ) -> DispatchResultWithPostInfo {768 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);769770 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);771 let budget = budget::Value::new(NESTING_BUDGET);772773 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))774 }775776 /// Delete specified token properties. Currently properties only work with NFTs.777 ///778 /// # Permissions779 ///780 /// * Depends on collection's token property permissions and specified property mutability:781 /// * Collection owner782 /// * Collection admin783 /// * Token owner784 ///785 /// # Arguments786 ///787 /// * `collection_id`: ID of the collection to which the token belongs.788 /// * `token_id`: ID of the modified token.789 /// * `property_keys`: Vector of keys of the properties to be deleted.790 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.791 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]792 #[transactional]793 pub fn delete_token_properties(794 origin,795 collection_id: CollectionId,796 token_id: TokenId,797 property_keys: Vec<PropertyKey>798 ) -> DispatchResultWithPostInfo {799 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);800801 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);802 let budget = budget::Value::new(NESTING_BUDGET);803804 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))805 }806807 /// Add or change token property permissions of a collection.808 ///809 /// Without a permission for a particular key, a property with that key810 /// cannot be created in a token.811 ///812 /// # Permissions813 ///814 /// * Collection owner815 /// * Collection admin816 ///817 /// # Arguments818 ///819 /// * `collection_id`: ID of the modified collection.820 /// * `property_permissions`: Vector of permissions for property keys.821 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.822 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]823 #[transactional]824 pub fn set_token_property_permissions(825 origin,826 collection_id: CollectionId,827 property_permissions: Vec<PropertyKeyPermission>,828 ) -> DispatchResultWithPostInfo {829 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);830831 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);832833 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))834 }835836 /// Create multiple items within a collection with explicitly specified initial parameters.837 ///838 /// # Permissions839 ///840 /// * Collection owner841 /// * Collection admin842 /// * Anyone if843 /// * Allow List is enabled, and844 /// * Address is added to allow list, and845 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])846 ///847 /// # Arguments848 ///849 /// * `collection_id`: ID of the collection to which the tokens would belong.850 /// * `data`: Explicit item creation data.851 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]852 #[transactional]853 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {854 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);855 let budget = budget::Value::new(NESTING_BUDGET);856857 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))858 }859860 /// Completely allow or disallow transfers for a particular collection.861 ///862 /// # Permissions863 ///864 /// * Collection owner865 ///866 /// # Arguments867 ///868 /// * `collection_id`: ID of the collection.869 /// * `value`: New value of the flag, are transfers allowed?870 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]871 #[transactional]872 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {873 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);874 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;875 target_collection.check_is_internal()?;876 target_collection.check_is_owner(&sender)?;877878 // =========879880 target_collection.limits.transfers_enabled = Some(value);881 target_collection.save()882 }883884 /// Destroy an item.885 ///886 /// # Permissions887 ///888 /// * Collection owner889 /// * Collection admin890 /// * Current item owner891 ///892 /// # Arguments893 ///894 /// * `collection_id`: ID of the collection to which the item belongs.895 /// * `item_id`: ID of item to burn.896 /// * `value`: Number of pieces of the item to destroy.897 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.898 /// * Fungible Mode: The desired number of pieces to burn.899 /// * Re-Fungible Mode: The desired number of pieces to burn.900 #[weight = T::CommonWeightInfo::burn_item()]901 #[transactional]902 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {903 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);904905 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;906 if value == 1 {907 <NftTransferBasket<T>>::remove(collection_id, item_id);908 <NftApproveBasket<T>>::remove(collection_id, item_id);909 }910 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?911 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());912 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));913 Ok(post_info)914 }915916 /// Destroy a token on behalf of the owner as a non-owner account.917 ///918 /// See also: [`approve`][`Pallet::approve`].919 ///920 /// After this method executes, one approval is removed from the total so that921 /// the approved address will not be able to transfer this item again from this owner.922 ///923 /// # Permissions924 ///925 /// * Collection owner926 /// * Collection admin927 /// * Current token owner928 /// * Address approved by current item owner929 ///930 /// # Arguments931 ///932 /// * `from`: The owner of the burning item.933 /// * `collection_id`: ID of the collection to which the item belongs.934 /// * `item_id`: ID of item to burn.935 /// * `value`: Number of pieces to burn.936 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.937 /// * Fungible Mode: The desired number of pieces to burn.938 /// * Re-Fungible Mode: The desired number of pieces to burn.939 #[weight = T::CommonWeightInfo::burn_from()]940 #[transactional]941 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {942 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);943 let budget = budget::Value::new(NESTING_BUDGET);944945 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))946 }947948 /// Change ownership of the token.949 ///950 /// # Permissions951 ///952 /// * Collection owner953 /// * Collection admin954 /// * Current token owner955 ///956 /// # Arguments957 ///958 /// * `recipient`: Address of token recipient.959 /// * `collection_id`: ID of the collection the item belongs to.960 /// * `item_id`: ID of the item.961 /// * Non-Fungible Mode: Required.962 /// * Fungible Mode: Ignored.963 /// * Re-Fungible Mode: Required.964 ///965 /// * `value`: Amount to transfer.966 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.967 /// * Fungible Mode: The desired number of pieces to transfer.968 /// * Re-Fungible Mode: The desired number of pieces to transfer.969 #[weight = T::CommonWeightInfo::transfer()]970 #[transactional]971 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {972 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);973 let budget = budget::Value::new(NESTING_BUDGET);974975 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))976 }977978 /// Allow a non-permissioned address to transfer or burn an item.979 ///980 /// # Permissions981 ///982 /// * Collection owner983 /// * Collection admin984 /// * Current item owner985 ///986 /// # Arguments987 ///988 /// * `spender`: Account to be approved to make specific transactions on non-owned tokens.989 /// * `collection_id`: ID of the collection the item belongs to.990 /// * `item_id`: ID of the item transactions on which are now approved.991 /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).992 /// Set to 0 to revoke the approval.993 #[weight = T::CommonWeightInfo::approve()]994 #[transactional]995 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {996 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);997998 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))999 }10001001 /// Change ownership of an item on behalf of the owner as a non-owner account.1002 ///1003 /// See the [`approve`][`Pallet::approve`] method for additional information.1004 ///1005 /// After this method executes, one approval is removed from the total so that1006 /// the approved address will not be able to transfer this item again from this owner.1007 ///1008 /// # Permissions1009 ///1010 /// * Collection owner1011 /// * Collection admin1012 /// * Current item owner1013 /// * Address approved by current item owner1014 ///1015 /// # Arguments1016 ///1017 /// * `from`: Address that currently owns the token.1018 /// * `recipient`: Address of the new token-owner-to-be.1019 /// * `collection_id`: ID of the collection the item.1020 /// * `item_id`: ID of the item to be transferred.1021 /// * `value`: Amount to transfer.1022 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1023 /// * Fungible Mode: The desired number of pieces to transfer.1024 /// * Re-Fungible Mode: The desired number of pieces to transfer.1025 #[weight = T::CommonWeightInfo::transfer_from()]1026 #[transactional]1027 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1028 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1029 let budget = budget::Value::new(NESTING_BUDGET);10301031 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1032 }10331034 /// Set specific limits of a collection. Empty, or None fields mean chain default.1035 ///1036 /// # Permissions1037 ///1038 /// * Collection owner1039 /// * Collection admin1040 ///1041 /// # Arguments1042 ///1043 /// * `collection_id`: ID of the modified collection.1044 /// * `new_limit`: New limits of the collection. Fields that are not set (None)1045 /// will not overwrite the old ones.1046 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1047 #[transactional]1048 pub fn set_collection_limits(1049 origin,1050 collection_id: CollectionId,1051 new_limit: CollectionLimits,1052 ) -> DispatchResult {1053 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1054 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1055 target_collection.check_is_internal()?;1056 target_collection.check_is_owner_or_admin(&sender)?;1057 let old_limit = &target_collection.limits;10581059 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10601061 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1062 collection_id1063 ));10641065 target_collection.save()1066 }10671068 /// Set specific permissions of a collection. Empty, or None fields mean chain default.1069 ///1070 /// # Permissions1071 ///1072 /// * Collection owner1073 /// * Collection admin1074 ///1075 /// # Arguments1076 ///1077 /// * `collection_id`: ID of the modified collection.1078 /// * `new_permission`: New permissions of the collection. Fields that are not set (None)1079 /// will not overwrite the old ones.1080 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1081 #[transactional]1082 pub fn set_collection_permissions(1083 origin,1084 collection_id: CollectionId,1085 new_permission: CollectionPermissions,1086 ) -> DispatchResult {1087 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1088 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1089 target_collection.check_is_internal()?;1090 target_collection.check_is_owner_or_admin(&sender)?;1091 let old_limit = &target_collection.permissions;10921093 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10941095 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1096 collection_id1097 ));10981099 target_collection.save()1100 }11011102 /// Re-partition a refungible token, while owning all of its parts/pieces.1103 ///1104 /// # Permissions1105 ///1106 /// * Token owner (must own every part)1107 ///1108 /// # Arguments1109 ///1110 /// * `collection_id`: ID of the collection the RFT belongs to.1111 /// * `token_id`: ID of the RFT.1112 /// * `amount`: New number of parts/pieces into which the token shall be partitioned.1113 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]1114 #[transactional]1115 pub fn repartition(1116 origin,1117 collection_id: CollectionId,1118 token_id: TokenId,1119 amount: u128,1120 ) -> DispatchResultWithPostInfo {1121 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1122 dispatch_tx::<T, _>(collection_id, |d| {1123 if let Some(refungible_extensions) = d.refungible_extensions() {1124 refungible_extensions.repartition(&sender, token_id, amount)1125 } else {1126 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1127 }1128 })1129 }1130 }1131}