difftreelog
refactor remove `#[transactional]` from extrinsics
in: master
Every extrinsic now runs in transaction implicitly, and `#[transactional]` on pallet dispatchable is now meaningless
4 files changed
pallets/evm-migration/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-migration/src/lib.rs
+++ b/pallets/evm-migration/src/lib.rs
@@ -23,7 +23,7 @@
#[frame_support::pallet]
pub mod pallet {
- use frame_support::{pallet_prelude::*, transactional};
+ use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sp_core::{H160, H256};
use sp_std::vec::Vec;
@@ -84,7 +84,6 @@
}
#[pallet::weight(<SelfWeightOf<T>>::finish(code.len() as u32))]
- #[transactional]
pub fn finish(origin: OriginFor<T>, address: H160, code: Vec<u8>) -> DispatchResult {
ensure_root(origin)?;
ensure!(
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -145,7 +145,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{pallet_prelude::*, BoundedVec, dispatch::DispatchResult};
use frame_system::{pallet_prelude::*, ensure_signed};
use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
use sp_std::{
@@ -350,11 +350,11 @@
/// * Anyone - will be assigned as the issuer of the collection.
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `metadata`: Metadata describing the collection, e.g. IPFS hash. Cannot be changed.
/// - `max`: Optional maximum number of tokens.
/// - `symbol`: UTF-8 string with token prefix, by which to represent the token in wallets and UIs.
/// Analogous to Unique's [`token_prefix`](up_data_structs::Collection). Cannot be changed.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::create_collection())]
pub fn create_collection(
origin: OriginFor<T>,
@@ -426,8 +426,8 @@
/// * Collection issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `collection_id`: RMRK ID of the collection to destroy.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]
pub fn destroy_collection(
origin: OriginFor<T>,
@@ -459,9 +459,9 @@
/// * Collection issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `collection_id`: RMRK collection ID to change the issuer of.
/// - `new_issuer`: Collection's new issuer.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]
pub fn change_collection_issuer(
origin: OriginFor<T>,
@@ -497,8 +497,8 @@
/// * Collection issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `collection_id`: RMRK ID of the collection to lock.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::lock_collection())]
pub fn lock_collection(
origin: OriginFor<T>,
@@ -535,6 +535,7 @@
/// * Collection issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `owner`: Owner account of the NFT. If set to None, defaults to the sender (collection issuer).
/// - `collection_id`: RMRK collection ID for the NFT to be minted within. Cannot be changed.
/// - `recipient`: Receiver account of the royalty. Has no effect if the `royalty_amount` is not set. Cannot be changed.
@@ -542,7 +543,6 @@
/// - `metadata`: Arbitrary data about an NFT, e.g. IPFS hash. Cannot be changed.
/// - `transferable`: Can this NFT be transferred? Cannot be changed.
/// - `resources`: Resource data to be added to the NFT immediately after minting.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]
pub fn mint_nft(
origin: OriginFor<T>,
@@ -620,12 +620,12 @@
/// * Token owner
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `collection_id`: RMRK ID of the collection in which the NFT to burn belongs to.
/// - `nft_id`: ID of the NFT to be destroyed.
/// - `max_burns`: Maximum number of tokens to burn, assuming nesting. The transaction
/// is reverted if there are more tokens to burn in the nesting tree than this number.
/// This is primarily a mechanism of transaction weight control.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]
pub fn burn_nft(
origin: OriginFor<T>,
@@ -669,10 +669,10 @@
/// - Token owner
///
/// # Arguments:
- /// - `collection_id`: RMRK ID of the collection of the NFT to be transferred.
- /// - `nft_id`: ID of the NFT to be transferred.
+ /// - `origin`: sender of the transaction
+ /// - `rmrk_collection_id`: RMRK ID of the collection of the NFT to be transferred.
+ /// - `rmrk_nft_id`: ID of the NFT to be transferred.
/// - `new_owner`: New owner of the nft which can be either an account or a NFT.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::send())]
pub fn send(
origin: OriginFor<T>,
@@ -793,11 +793,11 @@
/// - Token-owner-to-be
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT to be accepted.
/// - `rmrk_nft_id`: ID of the NFT to be accepted.
/// - `new_owner`: Either the sender's account ID or a sender-owned NFT,
/// whichever the accepted NFT was sent to.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::accept_nft())]
pub fn accept_nft(
origin: OriginFor<T>,
@@ -885,9 +885,9 @@
/// - Token-owner-to-be-not
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK ID of the NFT to be rejected.
/// - `rmrk_nft_id`: ID of the NFT to be rejected.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::reject_nft())]
pub fn reject_nft(
origin: OriginFor<T>,
@@ -950,10 +950,11 @@
/// - Token owner
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `rmrk_nft_id`: ID of the NFT with a pending resource to be accepted.
/// - `resource_id`: ID of the newly created pending resource.
- #[transactional]
+ /// accept the addition of a new resource to an existing NFT
#[pallet::weight(<SelfWeightOf<T>>::accept_resource())]
pub fn accept_resource(
origin: OriginFor<T>,
@@ -1004,10 +1005,10 @@
/// - Token owner
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `rmrk_nft_id`: ID of the NFT with a resource to be removed.
/// - `resource_id`: ID of the removal-pending resource.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]
pub fn accept_resource_removal(
origin: OriginFor<T>,
@@ -1084,11 +1085,11 @@
/// - Token owner - in case of NFT property
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID.
/// - `maybe_nft_id`: Optional ID of the NFT. If left empty, then the property is set for the collection.
/// - `key`: Key of the custom property to be referenced by.
/// - `value`: Value of the custom property to be stored.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::set_property())]
pub fn set_property(
origin: OriginFor<T>,
@@ -1158,10 +1159,10 @@
/// - Token owner
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `rmrk_nft_id`: ID of the NFT to rearrange resource priorities for.
/// - `priorities`: Ordered vector of resource IDs.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::set_priority())]
pub fn set_priority(
origin: OriginFor<T>,
@@ -1209,10 +1210,10 @@
/// the owner's [acceptance](Pallet::accept_resource).
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `nft_id`: ID of the NFT to assign a resource to.
/// - `resource`: Data of the resource to be created.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]
pub fn add_basic_resource(
origin: OriginFor<T>,
@@ -1251,10 +1252,10 @@
/// the owner's [acceptance](Pallet::accept_resource).
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `nft_id`: ID of the NFT to assign a resource to.
/// - `resource`: Data of the resource to be created.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]
pub fn add_composable_resource(
origin: OriginFor<T>,
@@ -1313,10 +1314,10 @@
/// the owner's [acceptance](Pallet::accept_resource).
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `rmrk_collection_id`: RMRK collection ID of the NFT.
/// - `nft_id`: ID of the NFT to assign a resource to.
/// - `resource`: Data of the resource to be created.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]
pub fn add_slot_resource(
origin: OriginFor<T>,
@@ -1354,10 +1355,10 @@
/// - Collection issuer
///
/// # Arguments
- /// - `collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
+ /// - `origin`: sender of the transaction
+ /// - `rmrk_collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
/// - `nft_id`: ID of the NFT with a resource to be removed.
/// - `resource_id`: ID of the resource to be removed.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::remove_resource())]
pub fn remove_resource(
origin: OriginFor<T>,
pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -133,7 +133,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
+use frame_support::{pallet_prelude::*, BoundedVec, dispatch::DispatchResult};
use frame_system::{pallet_prelude::*, ensure_signed};
use sp_runtime::DispatchError;
use up_data_structs::*;
@@ -226,11 +226,11 @@
/// - Anyone - will be assigned as the issuer of the Base.
///
/// # Arguments:
+ /// - `origin`: Caller, will be assigned as the issuer of the Base
/// - `base_type`: Arbitrary media type, e.g. "svg".
/// - `symbol`: Arbitrary client-chosen symbol.
/// - `parts`: Array of Fixed and Slot Parts composing the Base,
/// confined in length by [`RmrkPartsLimit`](up_data_structs::RmrkPartsLimit).
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]
pub fn create_base(
origin: OriginFor<T>,
@@ -295,13 +295,13 @@
/// - Base issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `base_id`: Base ID containing the Theme to be updated.
/// - `theme`: Theme to add to the Base. A Theme has a name and properties, which are an
/// array of [key, value, inherit].
/// - `key`: Arbitrary BoundedString, defined by client.
/// - `value`: Arbitrary BoundedString, defined by client.
/// - `inherit`: Optional bool.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]
pub fn theme_add(
origin: OriginFor<T>,
@@ -359,10 +359,10 @@
/// - Base issuer
///
/// # Arguments:
+ /// - `origin`: sender of the transaction
/// - `base_id`: Base containing the Slot Part to be updated.
- /// - `part_id`: Slot Part whose Equippable List is being updated.
+ /// - `slot_id`: Slot Part whose Equippable List is being updated .
/// - `equippables`: List of equippables that will override the current Equippables list.
- #[transactional]
#[pallet::weight(<SelfWeightOf<T>>::equippable())]
pub fn equippable(
origin: OriginFor<T>,
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 // TODO: basket cleanup should be moved elsewhere377 // Maybe runtime dispatch.rs should perform it?378379 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);380 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);381 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);382383 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);384 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);385 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);386387 Ok(())388 }389390 /// Add an address to allow list.391 ///392 /// # Permissions393 ///394 /// * Collection owner395 /// * Collection admin396 ///397 /// # Arguments398 ///399 /// * `collection_id`: ID of the modified collection.400 /// * `address`: ID of the address to be added to the allowlist.401 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]402 #[transactional]403 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{404405 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);406 let collection = <CollectionHandle<T>>::try_get(collection_id)?;407 collection.check_is_internal()?;408409 <PalletCommon<T>>::toggle_allowlist(410 &collection,411 &sender,412 &address,413 true,414 )?;415416 Self::deposit_event(Event::<T>::AllowListAddressAdded(417 collection_id,418 address419 ));420421 Ok(())422 }423424 /// Remove an address from allow list.425 ///426 /// # Permissions427 ///428 /// * Collection owner429 /// * Collection admin430 ///431 /// # Arguments432 ///433 /// * `collection_id`: ID of the modified collection.434 /// * `address`: ID of the address to be removed from the allowlist.435 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]436 #[transactional]437 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{438439 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);440 let collection = <CollectionHandle<T>>::try_get(collection_id)?;441 collection.check_is_internal()?;442443 <PalletCommon<T>>::toggle_allowlist(444 &collection,445 &sender,446 &address,447 false,448 )?;449450 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(451 collection_id,452 address453 ));454455 Ok(())456 }457458 /// Change the owner of the collection.459 ///460 /// # Permissions461 ///462 /// * Collection owner463 ///464 /// # Arguments465 ///466 /// * `collection_id`: ID of the modified collection.467 /// * `new_owner`: ID of the account that will become the owner.468 #[weight = <SelfWeightOf<T>>::change_collection_owner()]469 #[transactional]470 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {471472 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);473474 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;475 target_collection.check_is_internal()?;476 target_collection.check_is_owner(&sender)?;477478 target_collection.owner = new_owner.clone();479 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(480 collection_id,481 new_owner482 ));483484 target_collection.save()485 }486487 /// Add an admin to a collection.488 ///489 /// NFT Collection can be controlled by multiple admin addresses490 /// (some which can also be servers, for example). Admins can issue491 /// and burn NFTs, as well as add and remove other admins,492 /// but cannot change NFT or Collection ownership.493 ///494 /// # Permissions495 ///496 /// * Collection owner497 /// * Collection admin498 ///499 /// # Arguments500 ///501 /// * `collection_id`: ID of the Collection to add an admin for.502 /// * `new_admin`: Address of new admin to add.503 #[weight = <SelfWeightOf<T>>::add_collection_admin()]504 #[transactional]505 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin: T::CrossAccountId) -> DispatchResult {506 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);507 let collection = <CollectionHandle<T>>::try_get(collection_id)?;508 collection.check_is_internal()?;509510 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(511 collection_id,512 new_admin.clone()513 ));514515 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin, true)516 }517518 /// Remove admin of a collection.519 ///520 /// An admin address can remove itself. List of admins may become empty,521 /// in which case only Collection Owner will be able to add an Admin.522 ///523 /// # Permissions524 ///525 /// * Collection owner526 /// * Collection admin527 ///528 /// # Arguments529 ///530 /// * `collection_id`: ID of the collection to remove the admin for.531 /// * `account_id`: Address of the admin to remove.532 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]533 #[transactional]534 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {535 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);536 let collection = <CollectionHandle<T>>::try_get(collection_id)?;537 collection.check_is_internal()?;538539 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(540 collection_id,541 account_id.clone()542 ));543544 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)545 }546547 /// Set (invite) a new collection sponsor.548 ///549 /// If successful, confirmation from the sponsor-to-be will be pending.550 ///551 /// # Permissions552 ///553 /// * Collection owner554 /// * Collection admin555 ///556 /// # Arguments557 ///558 /// * `collection_id`: ID of the modified collection.559 /// * `new_sponsor`: ID of the account of the sponsor-to-be.560 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]561 #[transactional]562 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {563 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);564565 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;566 target_collection.check_is_owner_or_admin(&sender)?;567 target_collection.check_is_internal()?;568569 target_collection.set_sponsor(new_sponsor.clone())?;570571 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(572 collection_id,573 new_sponsor574 ));575576 target_collection.save()577 }578579 /// Confirm own sponsorship of a collection, becoming the sponsor.580 ///581 /// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].582 /// Sponsor can pay the fees of a transaction instead of the sender,583 /// but only within specified limits.584 ///585 /// # Permissions586 ///587 /// * Sponsor-to-be588 ///589 /// # Arguments590 ///591 /// * `collection_id`: ID of the collection with the pending sponsor.592 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]593 #[transactional]594 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {595 let sender = ensure_signed(origin)?;596597 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;598 target_collection.check_is_internal()?;599 ensure!(600 target_collection.confirm_sponsorship(&sender)?,601 Error::<T>::ConfirmUnsetSponsorFail602 );603604 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(605 collection_id,606 sender607 ));608609 target_collection.save()610 }611612 /// Remove a collection's a sponsor, making everyone pay for their own transactions.613 ///614 /// # Permissions615 ///616 /// * Collection owner617 ///618 /// # Arguments619 ///620 /// * `collection_id`: ID of the collection with the sponsor to remove.621 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]622 #[transactional]623 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {624 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);625626 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;627 target_collection.check_is_internal()?;628 target_collection.check_is_owner(&sender)?;629630 target_collection.sponsorship = SponsorshipState::Disabled;631632 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(633 collection_id634 ));635 target_collection.save()636 }637638 /// Mint an item within a collection.639 ///640 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].641 ///642 /// # Permissions643 ///644 /// * Collection owner645 /// * Collection admin646 /// * Anyone if647 /// * Allow List is enabled, and648 /// * Address is added to allow list, and649 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])650 ///651 /// # Arguments652 ///653 /// * `collection_id`: ID of the collection to which an item would belong.654 /// * `owner`: Address of the initial owner of the item.655 /// * `data`: Token data describing the item to store on chain.656 #[weight = T::CommonWeightInfo::create_item()]657 #[transactional]658 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {659 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);660 let budget = budget::Value::new(NESTING_BUDGET);661662 dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))663 }664665 /// Create multiple items within a collection.666 ///667 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].668 ///669 /// # Permissions670 ///671 /// * Collection owner672 /// * Collection admin673 /// * Anyone if674 /// * Allow List is enabled, and675 /// * Address is added to the allow list, and676 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])677 ///678 /// # Arguments679 ///680 /// * `collection_id`: ID of the collection to which the tokens would belong.681 /// * `owner`: Address of the initial owner of the tokens.682 /// * `items_data`: Vector of data describing each item to be created.683 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]684 #[transactional]685 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {686 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);687 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);688 let budget = budget::Value::new(NESTING_BUDGET);689690 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))691 }692693 /// Add or change collection properties.694 ///695 /// # Permissions696 ///697 /// * Collection owner698 /// * Collection admin699 ///700 /// # Arguments701 ///702 /// * `collection_id`: ID of the modified collection.703 /// * `properties`: Vector of key-value pairs stored as the collection's metadata.704 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.705 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]706 #[transactional]707 pub fn set_collection_properties(708 origin,709 collection_id: CollectionId,710 properties: Vec<Property>711 ) -> DispatchResultWithPostInfo {712 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);713714 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);715716 dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))717 }718719 /// Delete specified collection properties.720 ///721 /// # Permissions722 ///723 /// * Collection Owner724 /// * Collection Admin725 ///726 /// # Arguments727 ///728 /// * `collection_id`: ID of the modified collection.729 /// * `property_keys`: Vector of keys of the properties to be deleted.730 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.731 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]732 #[transactional]733 pub fn delete_collection_properties(734 origin,735 collection_id: CollectionId,736 property_keys: Vec<PropertyKey>,737 ) -> DispatchResultWithPostInfo {738 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);739740 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);741742 dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))743 }744745 /// Add or change token properties according to collection's permissions.746 /// Currently properties only work with NFTs.747 ///748 /// # Permissions749 ///750 /// * Depends on collection's token property permissions and specified property mutability:751 /// * Collection owner752 /// * Collection admin753 /// * Token owner754 ///755 /// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].756 ///757 /// # Arguments758 ///759 /// * `collection_id: ID of the collection to which the token belongs.760 /// * `token_id`: ID of the modified token.761 /// * `properties`: Vector of key-value pairs stored as the token's metadata.762 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.763 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]764 #[transactional]765 pub fn set_token_properties(766 origin,767 collection_id: CollectionId,768 token_id: TokenId,769 properties: Vec<Property>770 ) -> DispatchResultWithPostInfo {771 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);772773 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);774 let budget = budget::Value::new(NESTING_BUDGET);775776 dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget))777 }778779 /// Delete specified token properties. Currently properties only work with NFTs.780 ///781 /// # Permissions782 ///783 /// * Depends on collection's token property permissions and specified property mutability:784 /// * Collection owner785 /// * Collection admin786 /// * Token owner787 ///788 /// # Arguments789 ///790 /// * `collection_id`: ID of the collection to which the token belongs.791 /// * `token_id`: ID of the modified token.792 /// * `property_keys`: Vector of keys of the properties to be deleted.793 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.794 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]795 #[transactional]796 pub fn delete_token_properties(797 origin,798 collection_id: CollectionId,799 token_id: TokenId,800 property_keys: Vec<PropertyKey>801 ) -> DispatchResultWithPostInfo {802 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);803804 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);805 let budget = budget::Value::new(NESTING_BUDGET);806807 dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget))808 }809810 /// Add or change token property permissions of a collection.811 ///812 /// Without a permission for a particular key, a property with that key813 /// cannot be created in a token.814 ///815 /// # Permissions816 ///817 /// * Collection owner818 /// * Collection admin819 ///820 /// # Arguments821 ///822 /// * `collection_id`: ID of the modified collection.823 /// * `property_permissions`: Vector of permissions for property keys.824 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.825 #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)]826 #[transactional]827 pub fn set_token_property_permissions(828 origin,829 collection_id: CollectionId,830 property_permissions: Vec<PropertyKeyPermission>,831 ) -> DispatchResultWithPostInfo {832 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);833834 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);835836 dispatch_tx::<T, _>(collection_id, |d| d.set_token_property_permissions(&sender, property_permissions))837 }838839 /// Create multiple items within a collection with explicitly specified initial parameters.840 ///841 /// # Permissions842 ///843 /// * Collection owner844 /// * Collection admin845 /// * Anyone if846 /// * Allow List is enabled, and847 /// * Address is added to allow list, and848 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])849 ///850 /// # Arguments851 ///852 /// * `collection_id`: ID of the collection to which the tokens would belong.853 /// * `data`: Explicit item creation data.854 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]855 #[transactional]856 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {857 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);858 let budget = budget::Value::new(NESTING_BUDGET);859860 dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))861 }862863 /// Completely allow or disallow transfers for a particular collection.864 ///865 /// # Permissions866 ///867 /// * Collection owner868 ///869 /// # Arguments870 ///871 /// * `collection_id`: ID of the collection.872 /// * `value`: New value of the flag, are transfers allowed?873 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]874 #[transactional]875 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {876 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);877 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;878 target_collection.check_is_internal()?;879 target_collection.check_is_owner(&sender)?;880881 // =========882883 target_collection.limits.transfers_enabled = Some(value);884 target_collection.save()885 }886887 /// Destroy an item.888 ///889 /// # Permissions890 ///891 /// * Collection owner892 /// * Collection admin893 /// * Current item owner894 ///895 /// # Arguments896 ///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 of the item to destroy.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_item()]904 #[transactional]905 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {906 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);907908 let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;909 if value == 1 {910 <NftTransferBasket<T>>::remove(collection_id, item_id);911 <NftApproveBasket<T>>::remove(collection_id, item_id);912 }913 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?914 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());915 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));916 Ok(post_info)917 }918919 /// Destroy a token on behalf of the owner as a non-owner account.920 ///921 /// See also: [`approve`][`Pallet::approve`].922 ///923 /// After this method executes, one approval is removed from the total so that924 /// the approved address will not be able to transfer this item again from this owner.925 ///926 /// # Permissions927 ///928 /// * Collection owner929 /// * Collection admin930 /// * Current token owner931 /// * Address approved by current item owner932 ///933 /// # Arguments934 ///935 /// * `from`: The owner of the burning item.936 /// * `collection_id`: ID of the collection to which the item belongs.937 /// * `item_id`: ID of item to burn.938 /// * `value`: Number of pieces to burn.939 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.940 /// * Fungible Mode: The desired number of pieces to burn.941 /// * Re-Fungible Mode: The desired number of pieces to burn.942 #[weight = T::CommonWeightInfo::burn_from()]943 #[transactional]944 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let budget = budget::Value::new(NESTING_BUDGET);947948 dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))949 }950951 /// Change ownership of the token.952 ///953 /// # Permissions954 ///955 /// * Collection owner956 /// * Collection admin957 /// * Current token owner958 ///959 /// # Arguments960 ///961 /// * `recipient`: Address of token recipient.962 /// * `collection_id`: ID of the collection the item belongs to.963 /// * `item_id`: ID of the item.964 /// * Non-Fungible Mode: Required.965 /// * Fungible Mode: Ignored.966 /// * Re-Fungible Mode: Required.967 ///968 /// * `value`: Amount to transfer.969 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.970 /// * Fungible Mode: The desired number of pieces to transfer.971 /// * Re-Fungible Mode: The desired number of pieces to transfer.972 #[weight = T::CommonWeightInfo::transfer()]973 #[transactional]974 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {975 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);976 let budget = budget::Value::new(NESTING_BUDGET);977978 dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))979 }980981 /// Allow a non-permissioned address to transfer or burn an item.982 ///983 /// # Permissions984 ///985 /// * Collection owner986 /// * Collection admin987 /// * Current item owner988 ///989 /// # Arguments990 ///991 /// * `spender`: Account to be approved to make specific transactions on non-owned tokens.992 /// * `collection_id`: ID of the collection the item belongs to.993 /// * `item_id`: ID of the item transactions on which are now approved.994 /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).995 /// Set to 0 to revoke the approval.996 #[weight = T::CommonWeightInfo::approve()]997 #[transactional]998 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {999 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10001001 dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))1002 }10031004 /// Change ownership of an item on behalf of the owner as a non-owner account.1005 ///1006 /// See the [`approve`][`Pallet::approve`] method for additional information.1007 ///1008 /// After this method executes, one approval is removed from the total so that1009 /// the approved address will not be able to transfer this item again from this owner.1010 ///1011 /// # Permissions1012 ///1013 /// * Collection owner1014 /// * Collection admin1015 /// * Current item owner1016 /// * Address approved by current item owner1017 ///1018 /// # Arguments1019 ///1020 /// * `from`: Address that currently owns the token.1021 /// * `recipient`: Address of the new token-owner-to-be.1022 /// * `collection_id`: ID of the collection the item.1023 /// * `item_id`: ID of the item to be transferred.1024 /// * `value`: Amount to transfer.1025 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1026 /// * Fungible Mode: The desired number of pieces to transfer.1027 /// * Re-Fungible Mode: The desired number of pieces to transfer.1028 #[weight = T::CommonWeightInfo::transfer_from()]1029 #[transactional]1030 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {1031 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1032 let budget = budget::Value::new(NESTING_BUDGET);10331034 dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))1035 }10361037 /// Set specific limits of a collection. Empty, or None fields mean chain default.1038 ///1039 /// # Permissions1040 ///1041 /// * Collection owner1042 /// * Collection admin1043 ///1044 /// # Arguments1045 ///1046 /// * `collection_id`: ID of the modified collection.1047 /// * `new_limit`: New limits of the collection. Fields that are not set (None)1048 /// will not overwrite the old ones.1049 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1050 #[transactional]1051 pub fn set_collection_limits(1052 origin,1053 collection_id: CollectionId,1054 new_limit: CollectionLimits,1055 ) -> DispatchResult {1056 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1057 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1058 target_collection.check_is_internal()?;1059 target_collection.check_is_owner_or_admin(&sender)?;1060 let old_limit = &target_collection.limits;10611062 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10631064 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1065 collection_id1066 ));10671068 target_collection.save()1069 }10701071 /// Set specific permissions of a collection. Empty, or None fields mean chain default.1072 ///1073 /// # Permissions1074 ///1075 /// * Collection owner1076 /// * Collection admin1077 ///1078 /// # Arguments1079 ///1080 /// * `collection_id`: ID of the modified collection.1081 /// * `new_permission`: New permissions of the collection. Fields that are not set (None)1082 /// will not overwrite the old ones.1083 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1084 #[transactional]1085 pub fn set_collection_permissions(1086 origin,1087 collection_id: CollectionId,1088 new_permission: CollectionPermissions,1089 ) -> DispatchResult {1090 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1091 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1092 target_collection.check_is_internal()?;1093 target_collection.check_is_owner_or_admin(&sender)?;1094 let old_limit = &target_collection.permissions;10951096 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_permission)?;10971098 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(1099 collection_id1100 ));11011102 target_collection.save()1103 }11041105 /// Re-partition a refungible token, while owning all of its parts/pieces.1106 ///1107 /// # Permissions1108 ///1109 /// * Token owner (must own every part)1110 ///1111 /// # Arguments1112 ///1113 /// * `collection_id`: ID of the collection the RFT belongs to.1114 /// * `token_id`: ID of the RFT.1115 /// * `amount`: New number of parts/pieces into which the token shall be partitioned.1116 #[weight = T::RefungibleExtensionsWeightInfo::repartition()]1117 #[transactional]1118 pub fn repartition(1119 origin,1120 collection_id: CollectionId,1121 token_id: TokenId,1122 amount: u128,1123 ) -> DispatchResultWithPostInfo {1124 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1125 dispatch_tx::<T, _>(collection_id, |d| {1126 if let Some(refungible_extensions) = d.refungible_extensions() {1127 refungible_extensions.repartition(&sender, token_id, amount)1128 } else {1129 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1130 }1131 })1132 }1133 }1134}