difftreelog
fix use CollectionIssuer enum for collection creation
in: master
6 files changed
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -11,7 +11,7 @@
use sp_weights::Weight;
use up_data_structs::{CollectionId, CreateCollectionData};
-use crate::{pallet::Config, CommonCollectionOperations};
+use crate::{pallet::Config, CollectionIssuer, CommonCollectionOperations};
// TODO: move to benchmarking
/// Price of [`dispatch_tx`] call with noop `call` argument
@@ -72,26 +72,11 @@
/// Create a regular collection. The collection will be created according to the value of [`data.mode`](CreateCollectionData::mode).
///
/// * `sender` - The user who will become the owner of the collection.
- /// * `payer` - If set, the user who pays the collection creation deposit.
+ /// * `issuer` - An entity that creates the collection.
/// * `data` - Description of the created collection.
fn create(
sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- data: CreateCollectionData<T::CrossAccountId>,
- ) -> Result<CollectionId, DispatchError> {
- Self::create_raw(sender, payer, false, data)
- }
-
- /// Function for creating regular and special collections.
- ///
- /// * `sender` - The user who will become the owner of the collection.
- /// * `payer` - If set, the user who pays the collection creation deposit.
- /// * `data` - Description of the created collection.
- /// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
- fn create_raw(
- sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError>;
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -944,6 +944,15 @@
}
}
+/// An issuer of a collection.
+pub enum CollectionIssuer<CrossAccountId> {
+ /// A user who creates the collection.
+ User(CrossAccountId),
+
+ /// The internal mechanisms are creating the collection.
+ Internals,
+}
+
fn check_token_permissions<T: Config>(
collection_admin_permitted: bool,
token_owner_permitted: bool,
@@ -1128,32 +1137,34 @@
/// Create new collection.
///
/// * `owner` - The owner of the collection.
- /// * `payer` - If set, the user that will pay a deposit for the collection creation.
- /// * `data` - Description of the created collection.
+ /// * `issuer` - An entity that creates the collection.
/// * `is_special_collection` -- Whether this collection is a special one, i.e. can have special flags set.
pub fn init_collection(
owner: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
- if !is_special_collection {
- ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
- }
+ match issuer {
+ CollectionIssuer::User(payer) => {
+ ensure!(data.flags.is_allowed_for_user(), <Error<T>>::NoPermission);
- // Take a (non-refundable) deposit of collection creation
- if let Some(payer) = payer {
- let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
- imbalance.subsume(<T as Config>::Currency::deposit(
- &T::TreasuryAccountId::get(),
- T::CollectionCreationPrice::get(),
- Precision::Exact,
- )?);
- let credit =
- <T as Config>::Currency::settle(payer.as_sub(), imbalance, Preservation::Preserve)
- .map_err(|_| Error::<T>::NotSufficientFounds)?;
+ // Take a (non-refundable) deposit of collection creation
+ let mut imbalance = <Debt<T::AccountId, <T as Config>::Currency>>::zero();
+ imbalance.subsume(<T as Config>::Currency::deposit(
+ &T::TreasuryAccountId::get(),
+ T::CollectionCreationPrice::get(),
+ Precision::Exact,
+ )?);
+ let credit = <T as Config>::Currency::settle(
+ payer.as_sub(),
+ imbalance,
+ Preservation::Preserve,
+ )
+ .map_err(|_| Error::<T>::NotSufficientFounds)?;
- debug_assert!(credit.peek().is_zero())
+ debug_assert!(credit.peek().is_zero());
+ }
+ CollectionIssuer::Internals => {}
}
{
pallets/foreign-assets/src/lib.rsdiffbeforeafterboth--- a/pallets/foreign-assets/src/lib.rs
+++ b/pallets/foreign-assets/src/lib.rs
@@ -56,6 +56,7 @@
#[frame_support::pallet]
pub mod module {
+ use pallet_common::CollectionIssuer;
use up_data_structs::{
CollectionDescription, Property, PropertyKeyPermission, PropertyPermission,
};
@@ -169,12 +170,9 @@
.try_into()
.expect("description length < max description length; qed");
- let payer = None;
- let is_special_collection = true;
- let collection_id = T::CollectionDispatch::create_raw(
+ let collection_id = T::CollectionDispatch::create(
foreign_collection_owner,
- payer,
- is_special_collection,
+ CollectionIssuer::Internals,
CreateCollectionData {
name,
token_prefix,
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -26,7 +26,7 @@
dispatch::CollectionDispatch,
erc::{static_property::key, CollectionHelpersEvents},
eth::{self, collection_id_to_address, map_eth_to_id},
- CollectionById, CollectionHandle, Pallet as PalletCommon,
+ CollectionById, CollectionHandle, CollectionIssuer, Pallet as PalletCommon,
};
use pallet_evm::{account::CrossAccountId, OnMethodCall, PrecompileHandle, PrecompileResult};
use pallet_evm_coder_substrate::{
@@ -110,9 +110,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
}
@@ -241,9 +244,12 @@
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
@@ -276,9 +282,12 @@
check_sent_amount_equals_collection_creation_price::<T>(value)?;
let collection_helpers_address =
T::CrossAccountId::from_eth(<T as pallet_common::Config>::ContractAddress::get());
- let collection_id =
- T::CollectionDispatch::create(caller, Some(collection_helpers_address), data)
- .map_err(dispatch_to_evm::<T>)?;
+ let collection_id = T::CollectionDispatch::create(
+ caller,
+ CollectionIssuer::User(collection_helpers_address),
+ data,
+ )
+ .map_err(dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
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::pallet_prelude::*;77use frame_system::pallet_prelude::*;78pub use pallet::*;79pub mod eth;8081#[cfg(feature = "runtime-benchmarks")]82pub mod benchmarking;83pub mod weights;8485#[frame_support::pallet]86pub mod pallet {87 use frame_support::{88 dispatch::{DispatchErrorWithPostInfo, DispatchResult, PostDispatchInfo},89 ensure, fail,90 storage::Key,91 BoundedVec,92 };93 use frame_system::{ensure_root, ensure_signed};94 use pallet_common::{95 dispatch::{dispatch_tx, CollectionDispatch},96 CollectionHandle, CollectionIssuer, CommonWeightInfo, Pallet as PalletCommon,97 RefungibleExtensionsWeightInfo,98 };99 use pallet_evm::account::CrossAccountId;100 use pallet_structure::weights::WeightInfo as StructureWeightInfo;101 use scale_info::TypeInfo;102 use sp_std::{vec, vec::Vec};103 use up_data_structs::{104 budget, CollectionId, CollectionLimits, CollectionMode, CollectionPermissions,105 CreateCollectionData, CreateItemData, CreateItemExData, Property, PropertyKey,106 PropertyKeyPermission, TokenId, COLLECTION_ADMINS_LIMIT, MAX_COLLECTION_DESCRIPTION_LENGTH,107 MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_PROPERTIES_SIZE, MAX_PROPERTIES_PER_ITEM,108 MAX_PROPERTY_KEY_LENGTH, MAX_PROPERTY_VALUE_LENGTH, MAX_TOKEN_PREFIX_LENGTH,109 MAX_TOKEN_PROPERTIES_SIZE,110 };111 use weights::WeightInfo;112113 use super::*;114115 /// Errors for the common Unique transactions.116 #[pallet::error]117 pub enum Error<T> {118 /// Decimal_points parameter must be lower than [`up_data_structs::MAX_DECIMAL_POINTS`].119 CollectionDecimalPointLimitExceeded,120 /// Length of items properties must be greater than 0.121 EmptyArgument,122 /// Repertition is only supported by refungible collection.123 RepartitionCalledOnNonRefungibleCollection,124 }125126 /// Configuration trait of this pallet.127 #[pallet::config]128 pub trait Config: frame_system::Config + pallet_common::Config + Sized + TypeInfo {129 /// Weight information for extrinsics in this pallet.130 type WeightInfo: WeightInfo;131132 /// Weight information for common pallet operations.133 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;134135 type StructureWeightInfo: StructureWeightInfo;136137 /// Weight info information for extra refungible pallet operations.138 type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo;139 }140141 #[pallet::pallet]142 pub struct Pallet<T>(_);143144 pub type SelfWeightOf<T> = <T as Config>::WeightInfo;145146 // # Used definitions147 //148 // ## User control levels149 //150 // chain-controlled - key is uncontrolled by user151 // i.e autoincrementing index152 // can use non-cryptographic hash153 // real - key is controlled by user154 // but it is hard to generate enough colliding values, i.e owner of signed txs155 // can use non-cryptographic hash156 // controlled - key is completly controlled by users157 // i.e maps with mutable keys158 // should use cryptographic hash159 //160 // ## User control level downgrade reasons161 //162 // ?1 - chain-controlled -> controlled163 // collections/tokens can be destroyed, resulting in massive holes164 // ?2 - chain-controlled -> controlled165 // same as ?1, but can be only added, resulting in easier exploitation166 // ?3 - real -> controlled167 // no confirmation required, so addresses can be easily generated168169 //#region Private members170 /// Used for migrations171 #[pallet::storage]172 pub type ChainVersion<T> = StorageValue<_, u64, ValueQuery>;173 //#endregion174175 //#region Tokens transfer sponosoring rate limit baskets176 /// (Collection id (controlled?2), who created (real))177 /// TODO: Off chain worker should remove from this map when collection gets removed178 #[pallet::storage]179 #[pallet::getter(fn create_item_busket)]180 pub type CreateItemBasket<T: Config> = StorageMap<181 Hasher = Blake2_128Concat,182 Key = (CollectionId, T::AccountId),183 Value = BlockNumberFor<T>,184 QueryKind = OptionQuery,185 >;186 /// Collection id (controlled?2), token id (controlled?2)187 #[pallet::storage]188 #[pallet::getter(fn nft_transfer_basket)]189 pub type NftTransferBasket<T: Config> = StorageDoubleMap<190 Hasher1 = Blake2_128Concat,191 Key1 = CollectionId,192 Hasher2 = Blake2_128Concat,193 Key2 = TokenId,194 Value = BlockNumberFor<T>,195 QueryKind = OptionQuery,196 >;197 /// Collection id (controlled?2), owning user (real)198 #[pallet::storage]199 #[pallet::getter(fn fungible_transfer_basket)]200 pub type FungibleTransferBasket<T: Config> = StorageDoubleMap<201 Hasher1 = Blake2_128Concat,202 Key1 = CollectionId,203 Hasher2 = Twox64Concat,204 Key2 = T::AccountId,205 Value = BlockNumberFor<T>,206 QueryKind = OptionQuery,207 >;208 /// Collection id (controlled?2), token id (controlled?2)209 #[pallet::storage]210 #[pallet::getter(fn refungible_transfer_basket)]211 pub type ReFungibleTransferBasket<T: Config> = StorageNMap<212 Key = (213 Key<Blake2_128Concat, CollectionId>,214 Key<Blake2_128Concat, TokenId>,215 Key<Twox64Concat, T::AccountId>,216 ),217 Value = BlockNumberFor<T>,218 QueryKind = OptionQuery,219 >;220 //#endregion221222 /// Last sponsoring of token property setting // todo:doc rephrase this and the following223 #[pallet::storage]224 #[pallet::getter(fn token_property_basket)]225 pub type TokenPropertyBasket<T: Config> = StorageDoubleMap<226 Hasher1 = Blake2_128Concat,227 Key1 = CollectionId,228 Hasher2 = Blake2_128Concat,229 Key2 = TokenId,230 Value = BlockNumberFor<T>,231 QueryKind = OptionQuery,232 >;233234 /// Last sponsoring of NFT approval in a collection235 #[pallet::storage]236 #[pallet::getter(fn nft_approve_basket)]237 pub type NftApproveBasket<T: Config> = StorageDoubleMap<238 Hasher1 = Blake2_128Concat,239 Key1 = CollectionId,240 Hasher2 = Blake2_128Concat,241 Key2 = TokenId,242 Value = BlockNumberFor<T>,243 QueryKind = OptionQuery,244 >;245 /// Last sponsoring of fungible tokens approval in a collection246 #[pallet::storage]247 #[pallet::getter(fn fungible_approve_basket)]248 pub type FungibleApproveBasket<T: Config> = StorageDoubleMap<249 Hasher1 = Blake2_128Concat,250 Key1 = CollectionId,251 Hasher2 = Twox64Concat,252 Key2 = T::AccountId,253 Value = BlockNumberFor<T>,254 QueryKind = OptionQuery,255 >;256 /// Last sponsoring of RFT approval in a collection257 #[pallet::storage]258 #[pallet::getter(fn refungible_approve_basket)]259 pub type RefungibleApproveBasket<T: Config> = StorageNMap<260 Key = (261 Key<Blake2_128Concat, CollectionId>,262 Key<Blake2_128Concat, TokenId>,263 Key<Twox64Concat, T::AccountId>,264 ),265 Value = BlockNumberFor<T>,266 QueryKind = OptionQuery,267 >;268269 #[pallet::extra_constants]270 impl<T: Config> Pallet<T> {271 /// A maximum number of levels of depth in the token nesting tree.272 fn nesting_budget() -> u32 {273 5274 }275276 /// Maximal length of a collection name.277 fn max_collection_name_length() -> u32 {278 MAX_COLLECTION_NAME_LENGTH279 }280281 /// Maximal length of a collection description.282 fn max_collection_description_length() -> u32 {283 MAX_COLLECTION_DESCRIPTION_LENGTH284 }285286 /// Maximal length of a token prefix.287 fn max_token_prefix_length() -> u32 {288 MAX_TOKEN_PREFIX_LENGTH289 }290291 /// Maximum admins per collection.292 fn collection_admins_limit() -> u32 {293 COLLECTION_ADMINS_LIMIT294 }295296 /// Maximal length of a property key.297 fn max_property_key_length() -> u32 {298 MAX_PROPERTY_KEY_LENGTH299 }300301 /// Maximal length of a property value.302 fn max_property_value_length() -> u32 {303 MAX_PROPERTY_VALUE_LENGTH304 }305306 /// A maximum number of token properties.307 fn max_properties_per_item() -> u32 {308 MAX_PROPERTIES_PER_ITEM309 }310311 /// Maximum size for all collection properties.312 fn max_collection_properties_size() -> u32 {313 MAX_COLLECTION_PROPERTIES_SIZE314 }315316 /// Maximum size of all token properties.317 fn max_token_properties_size() -> u32 {318 MAX_TOKEN_PROPERTIES_SIZE319 }320321 /// Default NFT collection limit.322 fn nft_default_collection_limits() -> CollectionLimits {323 CollectionLimits::with_default_limits(CollectionMode::NFT)324 }325326 /// Default RFT collection limit.327 fn rft_default_collection_limits() -> CollectionLimits {328 CollectionLimits::with_default_limits(CollectionMode::ReFungible)329 }330331 /// Default FT collection limit.332 fn ft_default_collection_limits() -> CollectionLimits {333 CollectionLimits::with_default_limits(CollectionMode::Fungible(0))334 }335 }336337 /// Type alias to Pallet, to be used by construct_runtime.338 #[pallet::call]339 impl<T: Config> Pallet<T> {340 /// Create a collection of tokens.341 ///342 /// Each Token may have multiple properties encoded as an array of bytes343 /// of certain length. The initial owner of the collection is set344 /// to the address that signed the transaction and can be changed later.345 ///346 /// Prefer the more advanced [`create_collection_ex`][`Pallet::create_collection_ex`] instead.347 ///348 /// # Permissions349 ///350 /// * Anyone - becomes the owner of the new collection.351 ///352 /// # Arguments353 ///354 /// * `collection_name`: Wide-character string with collection name355 /// (limit [`MAX_COLLECTION_NAME_LENGTH`]).356 /// * `collection_description`: Wide-character string with collection description357 /// (limit [`MAX_COLLECTION_DESCRIPTION_LENGTH`]).358 /// * `token_prefix`: Byte string containing the token prefix to mark a collection359 /// to which a token belongs (limit [`MAX_TOKEN_PREFIX_LENGTH`]).360 /// * `mode`: Type of items stored in the collection and type dependent data.361 ///362 /// returns collection ID363 ///364 /// Deprecated: `create_collection_ex` is more up-to-date and advanced, prefer it instead.365 #[pallet::call_index(0)]366 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]367 pub fn create_collection(368 origin: OriginFor<T>,369 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,370 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,371 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,372 mode: CollectionMode,373 ) -> DispatchResult {374 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {375 name: collection_name,376 description: collection_description,377 token_prefix,378 mode,379 ..Default::default()380 };381 Self::create_collection_ex(origin, data)382 }383384 /// Create a collection with explicit parameters.385 ///386 /// Prefer it to the deprecated [`create_collection`][`Pallet::create_collection`] method.387 ///388 /// # Permissions389 ///390 /// * Anyone - becomes the owner of the new collection.391 ///392 /// # Arguments393 ///394 /// * `data`: Explicit data of a collection used for its creation.395 #[pallet::call_index(1)]396 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]397 pub fn create_collection_ex(398 origin: OriginFor<T>,399 data: CreateCollectionData<T::CrossAccountId>,400 ) -> DispatchResult {401 let sender = ensure_signed(origin)?;402403 // =========404 let sender = T::CrossAccountId::from_sub(sender);405 let _id = T::CollectionDispatch::create(406 sender.clone(),407 CollectionIssuer::User(sender),408 data,409 )?;410411 Ok(())412 }413414 /// Destroy a collection if no tokens exist within.415 ///416 /// # Permissions417 ///418 /// * Collection owner419 ///420 /// # Arguments421 ///422 /// * `collection_id`: Collection to destroy.423 #[pallet::call_index(2)]424 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]425 pub fn destroy_collection(426 origin: OriginFor<T>,427 collection_id: CollectionId,428 ) -> DispatchResult {429 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);430431 Self::destroy_collection_internal(sender, collection_id)432 }433434 /// Add an address to allow list.435 ///436 /// # Permissions437 ///438 /// * Collection owner439 /// * Collection admin440 ///441 /// # Arguments442 ///443 /// * `collection_id`: ID of the modified collection.444 /// * `address`: ID of the address to be added to the allowlist.445 #[pallet::call_index(3)]446 #[pallet::weight(<SelfWeightOf<T>>::add_to_allow_list())]447 pub fn add_to_allow_list(448 origin: OriginFor<T>,449 collection_id: CollectionId,450 address: T::CrossAccountId,451 ) -> DispatchResult {452 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {453 fail!(<pallet_common::Error<T>>::UnsupportedOperation);454 }455456 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);457 let collection = <CollectionHandle<T>>::try_get(collection_id)?;458 collection.check_is_internal()?;459460 <PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, true)?;461462 Ok(())463 }464465 /// Remove an address from allow list.466 ///467 /// # Permissions468 ///469 /// * Collection owner470 /// * Collection admin471 ///472 /// # Arguments473 ///474 /// * `collection_id`: ID of the modified collection.475 /// * `address`: ID of the address to be removed from the allowlist.476 #[pallet::call_index(4)]477 #[pallet::weight(<SelfWeightOf<T>>::remove_from_allow_list())]478 pub fn remove_from_allow_list(479 origin: OriginFor<T>,480 collection_id: CollectionId,481 address: T::CrossAccountId,482 ) -> DispatchResult {483 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {484 fail!(<pallet_common::Error<T>>::UnsupportedOperation);485 }486487 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);488 let collection = <CollectionHandle<T>>::try_get(collection_id)?;489 collection.check_is_internal()?;490491 <PalletCommon<T>>::toggle_allowlist(&collection, &sender, &address, false)?;492493 Ok(())494 }495496 /// Change the owner of the collection.497 ///498 /// # Permissions499 ///500 /// * Collection owner501 ///502 /// # Arguments503 ///504 /// * `collection_id`: ID of the modified collection.505 /// * `new_owner`: ID of the account that will become the owner.506 #[pallet::call_index(5)]507 #[pallet::weight(<SelfWeightOf<T>>::change_collection_owner())]508 pub fn change_collection_owner(509 origin: OriginFor<T>,510 collection_id: CollectionId,511 new_owner: T::AccountId,512 ) -> DispatchResult {513 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {514 fail!(<pallet_common::Error<T>>::UnsupportedOperation);515 }516 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);517 let new_owner = T::CrossAccountId::from_sub(new_owner);518 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;519 target_collection.change_owner(sender, new_owner)520 }521522 /// Add an admin to a collection.523 ///524 /// NFT Collection can be controlled by multiple admin addresses525 /// (some which can also be servers, for example). Admins can issue526 /// and burn NFTs, as well as add and remove other admins,527 /// but cannot change NFT or Collection ownership.528 ///529 /// # Permissions530 ///531 /// * Collection owner532 /// * Collection admin533 ///534 /// # Arguments535 ///536 /// * `collection_id`: ID of the Collection to add an admin for.537 /// * `new_admin`: Address of new admin to add.538 #[pallet::call_index(6)]539 #[pallet::weight(<SelfWeightOf<T>>::add_collection_admin())]540 pub fn add_collection_admin(541 origin: OriginFor<T>,542 collection_id: CollectionId,543 new_admin_id: T::CrossAccountId,544 ) -> DispatchResult {545 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {546 fail!(<pallet_common::Error<T>>::UnsupportedOperation);547 }548 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);549 let collection = <CollectionHandle<T>>::try_get(collection_id)?;550 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)551 }552553 /// Remove admin of a collection.554 ///555 /// An admin address can remove itself. List of admins may become empty,556 /// in which case only Collection Owner will be able to add an Admin.557 ///558 /// # Permissions559 ///560 /// * Collection owner561 /// * Collection admin562 ///563 /// # Arguments564 ///565 /// * `collection_id`: ID of the collection to remove the admin for.566 /// * `account_id`: Address of the admin to remove.567 #[pallet::call_index(7)]568 #[pallet::weight(<SelfWeightOf<T>>::remove_collection_admin())]569 pub fn remove_collection_admin(570 origin: OriginFor<T>,571 collection_id: CollectionId,572 account_id: T::CrossAccountId,573 ) -> DispatchResult {574 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {575 fail!(<pallet_common::Error<T>>::UnsupportedOperation);576 }577 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);578 let collection = <CollectionHandle<T>>::try_get(collection_id)?;579 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)580 }581582 /// Set (invite) a new collection sponsor.583 ///584 /// If successful, confirmation from the sponsor-to-be will be pending.585 ///586 /// # Permissions587 ///588 /// * Collection owner589 /// * Collection admin590 ///591 /// # Arguments592 ///593 /// * `collection_id`: ID of the modified collection.594 /// * `new_sponsor`: ID of the account of the sponsor-to-be.595 #[pallet::call_index(8)]596 #[pallet::weight(<SelfWeightOf<T>>::set_collection_sponsor())]597 pub fn set_collection_sponsor(598 origin: OriginFor<T>,599 collection_id: CollectionId,600 new_sponsor: T::AccountId,601 ) -> DispatchResult {602 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {603 fail!(<pallet_common::Error<T>>::UnsupportedOperation);604 }605 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);606 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;607 target_collection.set_sponsor(&sender, new_sponsor.clone())608 }609610 /// Confirm own sponsorship of a collection, becoming the sponsor.611 ///612 /// An invitation must be pending, see [`set_collection_sponsor`][`Pallet::set_collection_sponsor`].613 /// Sponsor can pay the fees of a transaction instead of the sender,614 /// but only within specified limits.615 ///616 /// # Permissions617 ///618 /// * Sponsor-to-be619 ///620 /// # Arguments621 ///622 /// * `collection_id`: ID of the collection with the pending sponsor.623 #[pallet::call_index(9)]624 #[pallet::weight(<SelfWeightOf<T>>::confirm_sponsorship())]625 pub fn confirm_sponsorship(626 origin: OriginFor<T>,627 collection_id: CollectionId,628 ) -> DispatchResult {629 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {630 fail!(<pallet_common::Error<T>>::UnsupportedOperation);631 }632 let sender = ensure_signed(origin)?;633 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;634 target_collection.confirm_sponsorship(&sender)635 }636637 /// Remove a collection's a sponsor, making everyone pay for their own transactions.638 ///639 /// # Permissions640 ///641 /// * Collection owner642 ///643 /// # Arguments644 ///645 /// * `collection_id`: ID of the collection with the sponsor to remove.646 #[pallet::call_index(10)]647 #[pallet::weight(<SelfWeightOf<T>>::remove_collection_sponsor())]648 pub fn remove_collection_sponsor(649 origin: OriginFor<T>,650 collection_id: CollectionId,651 ) -> DispatchResult {652 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {653 fail!(<pallet_common::Error<T>>::UnsupportedOperation);654 }655 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);656 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;657 target_collection.remove_sponsor(&sender)658 }659660 /// Mint an item within a collection.661 ///662 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].663 ///664 /// # Permissions665 ///666 /// * Collection owner667 /// * Collection admin668 /// * Anyone if669 /// * Allow List is enabled, and670 /// * Address is added to allow list, and671 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])672 ///673 /// # Arguments674 ///675 /// * `collection_id`: ID of the collection to which an item would belong.676 /// * `owner`: Address of the initial owner of the item.677 /// * `data`: Token data describing the item to store on chain.678 #[pallet::call_index(11)]679 #[pallet::weight(T::CommonWeightInfo::create_item(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]680 pub fn create_item(681 origin: OriginFor<T>,682 collection_id: CollectionId,683 owner: T::CrossAccountId,684 data: CreateItemData,685 ) -> DispatchResultWithPostInfo {686 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);687 let budget = Self::structure_nesting_budget();688689 Self::refund_nesting_budget(690 dispatch_tx::<T, _>(collection_id, |d| {691 d.create_item(sender, owner, data, &budget)692 }),693 budget,694 )695 }696697 /// Create multiple items within a collection.698 ///699 /// A collection must exist first, see [`create_collection_ex`][`Pallet::create_collection_ex`].700 ///701 /// # Permissions702 ///703 /// * Collection owner704 /// * Collection admin705 /// * Anyone if706 /// * Allow List is enabled, and707 /// * Address is added to the allow list, and708 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])709 ///710 /// # Arguments711 ///712 /// * `collection_id`: ID of the collection to which the tokens would belong.713 /// * `owner`: Address of the initial owner of the tokens.714 /// * `items_data`: Vector of data describing each item to be created.715 #[pallet::call_index(12)]716 #[pallet::weight(T::CommonWeightInfo::create_multiple_items(items_data) + <Pallet<T>>::nesting_budget_predispatch_weight())]717 pub fn create_multiple_items(718 origin: OriginFor<T>,719 collection_id: CollectionId,720 owner: T::CrossAccountId,721 items_data: Vec<CreateItemData>,722 ) -> DispatchResultWithPostInfo {723 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);724 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);725 let budget = Self::structure_nesting_budget();726727 Self::refund_nesting_budget(728 dispatch_tx::<T, _>(collection_id, |d| {729 d.create_multiple_items(sender, owner, items_data, &budget)730 }),731 budget,732 )733 }734735 /// Add or change collection properties.736 ///737 /// # Permissions738 ///739 /// * Collection owner740 /// * Collection admin741 ///742 /// # Arguments743 ///744 /// * `collection_id`: ID of the modified collection.745 /// * `properties`: Vector of key-value pairs stored as the collection's metadata.746 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.747 #[pallet::call_index(13)]748 #[pallet::weight(T::CommonWeightInfo::set_collection_properties(properties.len() as u32))]749 pub fn set_collection_properties(750 origin: OriginFor<T>,751 collection_id: CollectionId,752 properties: Vec<Property>,753 ) -> DispatchResultWithPostInfo {754 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);755756 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);757758 dispatch_tx::<T, _>(collection_id, |d| {759 d.set_collection_properties(sender, properties)760 })761 }762763 /// Delete specified collection properties.764 ///765 /// # Permissions766 ///767 /// * Collection Owner768 /// * Collection Admin769 ///770 /// # Arguments771 ///772 /// * `collection_id`: ID of the modified collection.773 /// * `property_keys`: Vector of keys of the properties to be deleted.774 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.775 #[pallet::call_index(14)]776 #[pallet::weight(T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32))]777 pub fn delete_collection_properties(778 origin: OriginFor<T>,779 collection_id: CollectionId,780 property_keys: Vec<PropertyKey>,781 ) -> DispatchResultWithPostInfo {782 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);783784 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);785786 dispatch_tx::<T, _>(collection_id, |d| {787 d.delete_collection_properties(&sender, property_keys)788 })789 }790791 /// Add or change token properties according to collection's permissions.792 /// Currently properties only work with NFTs.793 ///794 /// # Permissions795 ///796 /// * Depends on collection's token property permissions and specified property mutability:797 /// * Collection owner798 /// * Collection admin799 /// * Token owner800 ///801 /// See [`set_token_property_permissions`][`Pallet::set_token_property_permissions`].802 ///803 /// # Arguments804 ///805 /// * `collection_id: ID of the collection to which the token belongs.806 /// * `token_id`: ID of the modified token.807 /// * `properties`: Vector of key-value pairs stored as the token's metadata.808 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.809 #[pallet::call_index(15)]810 #[pallet::weight(T::CommonWeightInfo::set_token_properties(properties.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]811 pub fn set_token_properties(812 origin: OriginFor<T>,813 collection_id: CollectionId,814 token_id: TokenId,815 properties: Vec<Property>,816 ) -> DispatchResultWithPostInfo {817 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);818819 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);820 let budget = Self::structure_nesting_budget();821822 Self::refund_nesting_budget(823 dispatch_tx::<T, _>(collection_id, |d| {824 d.set_token_properties(sender, token_id, properties, &budget)825 }),826 budget,827 )828 }829830 /// Delete specified token properties. Currently properties only work with NFTs.831 ///832 /// # Permissions833 ///834 /// * Depends on collection's token property permissions and specified property mutability:835 /// * Collection owner836 /// * Collection admin837 /// * Token owner838 ///839 /// # Arguments840 ///841 /// * `collection_id`: ID of the collection to which the token belongs.842 /// * `token_id`: ID of the modified token.843 /// * `property_keys`: Vector of keys of the properties to be deleted.844 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.845 #[pallet::call_index(16)]846 #[pallet::weight(T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32) + <Pallet<T>>::nesting_budget_predispatch_weight())]847 pub fn delete_token_properties(848 origin: OriginFor<T>,849 collection_id: CollectionId,850 token_id: TokenId,851 property_keys: Vec<PropertyKey>,852 ) -> DispatchResultWithPostInfo {853 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);854855 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);856 let budget = Self::structure_nesting_budget();857858 Self::refund_nesting_budget(859 dispatch_tx::<T, _>(collection_id, |d| {860 d.delete_token_properties(sender, token_id, property_keys, &budget)861 }),862 budget,863 )864 }865866 /// Add or change token property permissions of a collection.867 ///868 /// Without a permission for a particular key, a property with that key869 /// cannot be created in a token.870 ///871 /// # Permissions872 ///873 /// * Collection owner874 /// * Collection admin875 ///876 /// # Arguments877 ///878 /// * `collection_id`: ID of the modified collection.879 /// * `property_permissions`: Vector of permissions for property keys.880 /// Keys support Latin letters, `-`, `_`, and `.` as symbols.881 #[pallet::call_index(17)]882 #[pallet::weight(T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32))]883 pub fn set_token_property_permissions(884 origin: OriginFor<T>,885 collection_id: CollectionId,886 property_permissions: Vec<PropertyKeyPermission>,887 ) -> DispatchResultWithPostInfo {888 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);889890 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);891892 dispatch_tx::<T, _>(collection_id, |d| {893 d.set_token_property_permissions(&sender, property_permissions)894 })895 }896897 /// Create multiple items within a collection with explicitly specified initial parameters.898 ///899 /// # Permissions900 ///901 /// * Collection owner902 /// * Collection admin903 /// * Anyone if904 /// * Allow List is enabled, and905 /// * Address is added to allow list, and906 /// * MintPermission is enabled (see [`set_collection_permissions`][`Pallet::set_collection_permissions`])907 ///908 /// # Arguments909 ///910 /// * `collection_id`: ID of the collection to which the tokens would belong.911 /// * `data`: Explicit item creation data.912 #[pallet::call_index(18)]913 #[pallet::weight(T::CommonWeightInfo::create_multiple_items_ex(data) + <Pallet<T>>::nesting_budget_predispatch_weight())]914 pub fn create_multiple_items_ex(915 origin: OriginFor<T>,916 collection_id: CollectionId,917 data: CreateItemExData<T::CrossAccountId>,918 ) -> DispatchResultWithPostInfo {919 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);920 let budget = Self::structure_nesting_budget();921922 Self::refund_nesting_budget(923 dispatch_tx::<T, _>(collection_id, |d| {924 d.create_multiple_items_ex(sender, data, &budget)925 }),926 budget,927 )928 }929930 /// Completely allow or disallow transfers for a particular collection.931 ///932 /// # Permissions933 ///934 /// * Collection owner935 ///936 /// # Arguments937 ///938 /// * `collection_id`: ID of the collection.939 /// * `value`: New value of the flag, are transfers allowed?940 #[pallet::call_index(19)]941 #[pallet::weight(<SelfWeightOf<T>>::set_transfers_enabled_flag())]942 pub fn set_transfers_enabled_flag(943 origin: OriginFor<T>,944 collection_id: CollectionId,945 value: bool,946 ) -> DispatchResult {947 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {948 fail!(<pallet_common::Error<T>>::UnsupportedOperation);949 }950 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);951 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;952 target_collection.check_is_internal()?;953 target_collection.check_is_owner(&sender)?;954955 // =========956957 target_collection.limits.transfers_enabled = Some(value);958 target_collection.save()959 }960961 /// Destroy an item.962 ///963 /// # Permissions964 ///965 /// * Collection owner966 /// * Collection admin967 /// * Current item owner968 ///969 /// # Arguments970 ///971 /// * `collection_id`: ID of the collection to which the item belongs.972 /// * `item_id`: ID of item to burn.973 /// * `value`: Number of pieces of the item to destroy.974 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.975 /// * Fungible Mode: The desired number of pieces to burn.976 /// * Re-Fungible Mode: The desired number of pieces to burn.977 #[pallet::call_index(20)]978 #[pallet::weight(T::CommonWeightInfo::burn_item())]979 pub fn burn_item(980 origin: OriginFor<T>,981 collection_id: CollectionId,982 item_id: TokenId,983 value: u128,984 ) -> DispatchResultWithPostInfo {985 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);986987 let post_info =988 dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;989 if value == 1 {990 <NftTransferBasket<T>>::remove(collection_id, item_id);991 <NftApproveBasket<T>>::remove(collection_id, item_id);992 }993 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?994 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());995 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));996 Ok(post_info)997 }998999 /// Destroy a token on behalf of the owner as a non-owner account.1000 ///1001 /// See also: [`approve`][`Pallet::approve`].1002 ///1003 /// After this method executes, one approval is removed from the total so that1004 /// the approved address will not be able to transfer this item again from this owner.1005 ///1006 /// # Permissions1007 ///1008 /// * Collection owner1009 /// * Collection admin1010 /// * Current token owner1011 /// * Address approved by current item owner1012 ///1013 /// # Arguments1014 ///1015 /// * `from`: The owner of the burning item.1016 /// * `collection_id`: ID of the collection to which the item belongs.1017 /// * `item_id`: ID of item to burn.1018 /// * `value`: Number of pieces to burn.1019 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1020 /// * Fungible Mode: The desired number of pieces to burn.1021 /// * Re-Fungible Mode: The desired number of pieces to burn.1022 #[pallet::call_index(21)]1023 #[pallet::weight(T::CommonWeightInfo::burn_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1024 pub fn burn_from(1025 origin: OriginFor<T>,1026 collection_id: CollectionId,1027 from: T::CrossAccountId,1028 item_id: TokenId,1029 value: u128,1030 ) -> DispatchResultWithPostInfo {1031 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1032 let budget = Self::structure_nesting_budget();10331034 Self::refund_nesting_budget(1035 dispatch_tx::<T, _>(collection_id, |d| {1036 d.burn_from(sender, from, item_id, value, &budget)1037 }),1038 budget,1039 )1040 }10411042 /// Change ownership of the token.1043 ///1044 /// # Permissions1045 ///1046 /// * Collection owner1047 /// * Collection admin1048 /// * Current token owner1049 ///1050 /// # Arguments1051 ///1052 /// * `recipient`: Address of token recipient.1053 /// * `collection_id`: ID of the collection the item belongs to.1054 /// * `item_id`: ID of the item.1055 /// * Non-Fungible Mode: Required.1056 /// * Fungible Mode: Ignored.1057 /// * Re-Fungible Mode: Required.1058 ///1059 /// * `value`: Amount to transfer.1060 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1061 /// * Fungible Mode: The desired number of pieces to transfer.1062 /// * Re-Fungible Mode: The desired number of pieces to transfer.1063 #[pallet::call_index(22)]1064 #[pallet::weight(T::CommonWeightInfo::transfer() + <Pallet<T>>::nesting_budget_predispatch_weight())]1065 pub fn transfer(1066 origin: OriginFor<T>,1067 recipient: T::CrossAccountId,1068 collection_id: CollectionId,1069 item_id: TokenId,1070 value: u128,1071 ) -> DispatchResultWithPostInfo {1072 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1073 let budget = Self::structure_nesting_budget();10741075 Self::refund_nesting_budget(1076 dispatch_tx::<T, _>(collection_id, |d| {1077 d.transfer(sender, recipient, item_id, value, &budget)1078 }),1079 budget,1080 )1081 }10821083 /// Allow a non-permissioned address to transfer or burn an item.1084 ///1085 /// # Permissions1086 ///1087 /// * Collection owner1088 /// * Collection admin1089 /// * Current item owner1090 ///1091 /// # Arguments1092 ///1093 /// * `spender`: Account to be approved to make specific transactions on non-owned tokens.1094 /// * `collection_id`: ID of the collection the item belongs to.1095 /// * `item_id`: ID of the item transactions on which are now approved.1096 /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1097 /// Set to 0 to revoke the approval.1098 #[pallet::call_index(23)]1099 #[pallet::weight(T::CommonWeightInfo::approve())]1100 pub fn approve(1101 origin: OriginFor<T>,1102 spender: T::CrossAccountId,1103 collection_id: CollectionId,1104 item_id: TokenId,1105 amount: u128,1106 ) -> DispatchResultWithPostInfo {1107 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11081109 dispatch_tx::<T, _>(collection_id, |d| {1110 d.approve(sender, spender, item_id, amount)1111 })1112 }11131114 /// Allow a non-permissioned address to transfer or burn an item from owner's eth mirror.1115 ///1116 /// # Permissions1117 ///1118 /// * Collection owner1119 /// * Collection admin1120 /// * Current item owner1121 ///1122 /// # Arguments1123 ///1124 /// * `from`: Owner's account eth mirror1125 /// * `to`: Account to be approved to make specific transactions on non-owned tokens.1126 /// * `collection_id`: ID of the collection the item belongs to.1127 /// * `item_id`: ID of the item transactions on which are now approved.1128 /// * `amount`: Number of pieces of the item approved for a transaction (maximum of 1 for NFTs).1129 /// Set to 0 to revoke the approval.1130 #[pallet::call_index(24)]1131 #[pallet::weight(T::CommonWeightInfo::approve_from())]1132 pub fn approve_from(1133 origin: OriginFor<T>,1134 from: T::CrossAccountId,1135 to: T::CrossAccountId,1136 collection_id: CollectionId,1137 item_id: TokenId,1138 amount: u128,1139 ) -> DispatchResultWithPostInfo {1140 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11411142 dispatch_tx::<T, _>(collection_id, |d| {1143 d.approve_from(sender, from, to, item_id, amount)1144 })1145 }11461147 /// Change ownership of an item on behalf of the owner as a non-owner account.1148 ///1149 /// See the [`approve`][`Pallet::approve`] method for additional information.1150 ///1151 /// After this method executes, one approval is removed from the total so that1152 /// the approved address will not be able to transfer this item again from this owner.1153 ///1154 /// # Permissions1155 ///1156 /// * Collection owner1157 /// * Collection admin1158 /// * Current item owner1159 /// * Address approved by current item owner1160 ///1161 /// # Arguments1162 ///1163 /// * `from`: Address that currently owns the token.1164 /// * `recipient`: Address of the new token-owner-to-be.1165 /// * `collection_id`: ID of the collection the item.1166 /// * `item_id`: ID of the item to be transferred.1167 /// * `value`: Amount to transfer.1168 /// * Non-Fungible Mode: An NFT is indivisible, there is always 1 corresponding to an ID.1169 /// * Fungible Mode: The desired number of pieces to transfer.1170 /// * Re-Fungible Mode: The desired number of pieces to transfer.1171 #[pallet::call_index(25)]1172 #[pallet::weight(T::CommonWeightInfo::transfer_from() + <Pallet<T>>::nesting_budget_predispatch_weight())]1173 pub fn transfer_from(1174 origin: OriginFor<T>,1175 from: T::CrossAccountId,1176 recipient: T::CrossAccountId,1177 collection_id: CollectionId,1178 item_id: TokenId,1179 value: u128,1180 ) -> DispatchResultWithPostInfo {1181 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1182 let budget = Self::structure_nesting_budget();11831184 Self::refund_nesting_budget(1185 dispatch_tx::<T, _>(collection_id, |d| {1186 d.transfer_from(sender, from, recipient, item_id, value, &budget)1187 }),1188 budget,1189 )1190 }11911192 /// Set specific limits of a collection. Empty, or None fields mean chain default.1193 ///1194 /// # Permissions1195 ///1196 /// * Collection owner1197 /// * Collection admin1198 ///1199 /// # Arguments1200 ///1201 /// * `collection_id`: ID of the modified collection.1202 /// * `new_limit`: New limits of the collection. Fields that are not set (None)1203 /// will not overwrite the old ones.1204 #[pallet::call_index(26)]1205 #[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1206 pub fn set_collection_limits(1207 origin: OriginFor<T>,1208 collection_id: CollectionId,1209 new_limit: CollectionLimits,1210 ) -> DispatchResult {1211 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1212 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1213 }1214 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1215 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1216 <PalletCommon<T>>::update_limits(&sender, &mut target_collection, new_limit)1217 }12181219 /// Set specific permissions of a collection. Empty, or None fields mean chain default.1220 ///1221 /// # Permissions1222 ///1223 /// * Collection owner1224 /// * Collection admin1225 ///1226 /// # Arguments1227 ///1228 /// * `collection_id`: ID of the modified collection.1229 /// * `new_permission`: New permissions of the collection. Fields that are not set (None)1230 /// will not overwrite the old ones.1231 #[pallet::call_index(27)]1232 #[pallet::weight(<SelfWeightOf<T>>::set_collection_limits())]1233 pub fn set_collection_permissions(1234 origin: OriginFor<T>,1235 collection_id: CollectionId,1236 new_permission: CollectionPermissions,1237 ) -> DispatchResult {1238 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1239 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1240 }1241 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1242 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1243 <PalletCommon<T>>::update_permissions(&sender, &mut target_collection, new_permission)1244 }12451246 /// Re-partition a refungible token, while owning all of its parts/pieces.1247 ///1248 /// # Permissions1249 ///1250 /// * Token owner (must own every part)1251 ///1252 /// # Arguments1253 ///1254 /// * `collection_id`: ID of the collection the RFT belongs to.1255 /// * `token_id`: ID of the RFT.1256 /// * `amount`: New number of parts/pieces into which the token shall be partitioned.1257 #[pallet::call_index(28)]1258 #[pallet::weight(T::RefungibleExtensionsWeightInfo::repartition())]1259 pub fn repartition(1260 origin: OriginFor<T>,1261 collection_id: CollectionId,1262 token_id: TokenId,1263 amount: u128,1264 ) -> DispatchResultWithPostInfo {1265 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1266 dispatch_tx::<T, _>(collection_id, |d| {1267 if let Some(refungible_extensions) = d.refungible_extensions() {1268 refungible_extensions.repartition(&sender, token_id, amount)1269 } else {1270 fail!(<Error<T>>::RepartitionCalledOnNonRefungibleCollection)1271 }1272 })1273 }12741275 /// Sets or unsets the approval of a given operator.1276 ///1277 /// The `operator` is allowed to transfer all tokens of the `owner` on their behalf.1278 ///1279 /// # Arguments1280 ///1281 /// * `owner`: Token owner1282 /// * `operator`: Operator1283 /// * `approve`: Should operator status be granted or revoked?1284 #[pallet::call_index(29)]1285 #[pallet::weight(T::CommonWeightInfo::set_allowance_for_all())]1286 pub fn set_allowance_for_all(1287 origin: OriginFor<T>,1288 collection_id: CollectionId,1289 operator: T::CrossAccountId,1290 approve: bool,1291 ) -> DispatchResultWithPostInfo {1292 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1293 dispatch_tx::<T, _>(collection_id, |d| {1294 d.set_allowance_for_all(sender, operator, approve)1295 })1296 }12971298 /// Repairs a collection if the data was somehow corrupted.1299 ///1300 /// # Arguments1301 ///1302 /// * `collection_id`: ID of the collection to repair.1303 #[pallet::call_index(30)]1304 #[pallet::weight(<SelfWeightOf<T>>::force_repair_collection())]1305 pub fn force_repair_collection(1306 origin: OriginFor<T>,1307 collection_id: CollectionId,1308 ) -> DispatchResult {1309 if collection_id == pallet_common::NATIVE_FUNGIBLE_COLLECTION_ID {1310 fail!(<pallet_common::Error<T>>::UnsupportedOperation);1311 }1312 ensure_root(origin)?;1313 <PalletCommon<T>>::repair_collection(collection_id)1314 }13151316 /// Repairs a token if the data was somehow corrupted.1317 ///1318 /// # Arguments1319 ///1320 /// * `collection_id`: ID of the collection the item belongs to.1321 /// * `item_id`: ID of the item.1322 #[pallet::call_index(31)]1323 #[pallet::weight(T::CommonWeightInfo::force_repair_item())]1324 pub fn force_repair_item(1325 origin: OriginFor<T>,1326 collection_id: CollectionId,1327 item_id: TokenId,1328 ) -> DispatchResultWithPostInfo {1329 ensure_root(origin)?;1330 dispatch_tx::<T, _>(collection_id, |d| d.repair_item(item_id))1331 }1332 }13331334 impl<T: Config> Pallet<T> {1335 /// Force set `sponsor` for `collection`.1336 ///1337 /// Differs from [`set_collection_sponsor`][`Pallet::set_collection_sponsor`] in that confirmation1338 /// from the `sponsor` is not required.1339 ///1340 /// # Arguments1341 ///1342 /// * `sponsor`: ID of the account of the sponsor-to-be.1343 /// * `collection_id`: ID of the modified collection.1344 pub fn force_set_sponsor(1345 sponsor: T::AccountId,1346 collection_id: CollectionId,1347 ) -> DispatchResult {1348 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1349 target_collection.force_set_sponsor(sponsor)1350 }13511352 /// Force remove `sponsor` for `collection`.1353 ///1354 /// Differs from `remove_sponsor` in that1355 /// it doesn't require consent from the `owner` of the collection.1356 ///1357 /// # Arguments1358 ///1359 /// * `collection_id`: ID of the modified collection.1360 pub fn force_remove_collection_sponsor(collection_id: CollectionId) -> DispatchResult {1361 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1362 target_collection.force_remove_sponsor()1363 }13641365 #[inline(always)]1366 pub(crate) fn destroy_collection_internal(1367 sender: T::CrossAccountId,1368 collection_id: CollectionId,1369 ) -> DispatchResult {1370 T::CollectionDispatch::destroy(sender, collection_id)?;13711372 // TODO: basket cleanup should be moved elsewhere1373 // Maybe runtime dispatch.rs should perform it?13741375 let _ = <NftTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1376 let _ = <FungibleTransferBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1377 let _ = <ReFungibleTransferBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13781379 let _ = <NftApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1380 let _ = <FungibleApproveBasket<T>>::clear_prefix(collection_id, u32::MAX, None);1381 let _ = <RefungibleApproveBasket<T>>::clear_prefix((collection_id,), u32::MAX, None);13821383 Ok(())1384 }13851386 fn structure_nesting_budget() -> budget::Value {1387 budget::Value::new(Self::nesting_budget())1388 }13891390 fn nesting_budget_predispatch_weight() -> Weight {1391 T::StructureWeightInfo::find_parent().saturating_mul(Self::nesting_budget() as u64)1392 }13931394 pub fn refund_nesting_budget(1395 mut result: DispatchResultWithPostInfo,1396 budget: budget::Value,1397 ) -> DispatchResultWithPostInfo {1398 let refund_amount = budget.refund_amount();1399 let consumed = Self::nesting_budget() - refund_amount;14001401 match &mut result {1402 Ok(PostDispatchInfo {1403 actual_weight: Some(weight),1404 ..1405 })1406 | Err(DispatchErrorWithPostInfo {1407 post_info: PostDispatchInfo {1408 actual_weight: Some(weight),1409 ..1410 },1411 ..1412 }) => {1413 *weight += T::StructureWeightInfo::find_parent().saturating_mul(consumed as u64)1414 }1415 _ => {}1416 }14171418 result1419 }1420 }1421}runtime/common/dispatch.rsdiffbeforeafterboth--- a/runtime/common/dispatch.rs
+++ b/runtime/common/dispatch.rs
@@ -18,7 +18,7 @@
use pallet_balances_adapter::NativeFungibleHandle;
pub use pallet_common::dispatch::CollectionDispatch;
use pallet_common::{
- erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle,
+ erc::CommonEvmHandler, eth::map_eth_to_id, CollectionById, CollectionHandle, CollectionIssuer,
CommonCollectionOperations, Pallet as PalletCommon,
};
use pallet_evm::{PrecompileHandle, PrecompileResult};
@@ -66,10 +66,9 @@
}
}
- fn create_raw(
+ fn create(
sender: T::CrossAccountId,
- payer: Option<T::CrossAccountId>,
- is_special_collection: bool,
+ issuer: CollectionIssuer<T::CrossAccountId>,
data: CreateCollectionData<T::CrossAccountId>,
) -> Result<CollectionId, DispatchError> {
match data.mode {
@@ -87,7 +86,7 @@
_ => {}
};
- <PalletCommon<T>>::init_collection(sender, payer, is_special_collection, data)
+ <PalletCommon<T>>::init_collection(sender, issuer, data)
}
fn destroy(sender: T::CrossAccountId, collection_id: CollectionId) -> DispatchResult {