difftreelog
fix transfersubbundles
in: master
2 files changed
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -156,8 +156,8 @@
budget: &dyn Budget,
) -> Result<bool, DispatchError> {
let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
- Some((collection, token)) => Parent::Token(collection, token),
- None => Parent::User(user),
+ Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
+ None => user,
};
// Tried to nest token in itself
@@ -172,7 +172,7 @@
return Err(<Error<T>>::OuroborosDetected.into())
}
// Found needed parent, token is indirecty owned
- v if v == target_parent => return Ok(true),
+ Parent::User(user) if user == target_parent => return Ok(true),
// Token is owned by other user
Parent::User(_) => return Ok(false),
Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
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#![recursion_limit = "1024"]18#![cfg_attr(not(feature = "std"), no_std)]19#![allow(20 clippy::too_many_arguments,21 clippy::unnecessary_mut_passed,22 clippy::unused_unit23)]2425use frame_support::{26 decl_module, decl_storage, decl_error, decl_event,27 dispatch::DispatchResult,28 ensure,29 weights::{Weight},30 transactional,31 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},32 BoundedVec,33};34use scale_info::TypeInfo;35use frame_system::{self as system, ensure_signed};36use sp_runtime::{sp_std::prelude::Vec};37use up_data_structs::{38 MAX_COLLECTION_NAME_LENGTH,39 MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData,40 CollectionLimits, CollectionPermissions, CollectionId, CollectionMode, TokenId, SponsorshipState,41 CreateCollectionData, CreateItemExData, budget, Property, PropertyKey,42 PropertyKeyPermission,43};44use pallet_evm::account::CrossAccountId;45use pallet_common::{46 CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,47 dispatch::CollectionDispatch,48};4950#[cfg(feature = "runtime-benchmarks")]51mod benchmarking;52pub mod weights;53use weights::WeightInfo;5455const NESTING_BUDGET: u32 = 5;5657decl_error! {58 /// Error for non-fungible-token module.59 pub enum Error for Module<T: Config> {60 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.61 CollectionDecimalPointLimitExceeded,62 /// This address is not set as sponsor, use setCollectionSponsor first.63 ConfirmUnsetSponsorFail,64 /// Length of items properties must be greater than 0.65 EmptyArgument,66 }67}6869pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo {70 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;7172 /// Weight information for extrinsics in this pallet.73 type WeightInfo: WeightInfo;74 type CommonWeightInfo: CommonWeightInfo<Self::CrossAccountId>;75}7677decl_event! {78 pub enum Event<T>79 where80 <T as frame_system::Config>::AccountId,81 <T as pallet_evm::account::Config>::CrossAccountId,82 {83 /// Collection sponsor was removed84 ///85 /// # Arguments86 ///87 /// * collection_id: Globally unique collection identifier.88 CollectionSponsorRemoved(CollectionId),8990 /// Collection admin was added91 ///92 /// # Arguments93 ///94 /// * collection_id: Globally unique collection identifier.95 ///96 /// * admin: Admin address.97 CollectionAdminAdded(CollectionId, CrossAccountId),9899 /// Collection owned was change100 ///101 /// # Arguments102 ///103 /// * collection_id: Globally unique collection identifier.104 ///105 /// * owner: New owner address.106 CollectionOwnedChanged(CollectionId, AccountId),107108 /// Collection sponsor was set109 ///110 /// # Arguments111 ///112 /// * collection_id: Globally unique collection identifier.113 ///114 /// * owner: New sponsor address.115 CollectionSponsorSet(CollectionId, AccountId),116117 /// const on chain schema was set118 ///119 /// # Arguments120 ///121 /// * collection_id: Globally unique collection identifier.122 ConstOnChainSchemaSet(CollectionId),123124 /// New sponsor was confirm125 ///126 /// # Arguments127 ///128 /// * collection_id: Globally unique collection identifier.129 ///130 /// * sponsor: New sponsor address.131 SponsorshipConfirmed(CollectionId, AccountId),132133 /// Collection admin was removed134 ///135 /// # Arguments136 ///137 /// * collection_id: Globally unique collection identifier.138 ///139 /// * admin: Admin address.140 CollectionAdminRemoved(CollectionId, CrossAccountId),141142 /// Address was remove from allow list143 ///144 /// # Arguments145 ///146 /// * collection_id: Globally unique collection identifier.147 ///148 /// * user: Address.149 AllowListAddressRemoved(CollectionId, CrossAccountId),150151 /// Address was add to allow list152 ///153 /// # Arguments154 ///155 /// * collection_id: Globally unique collection identifier.156 ///157 /// * user: Address.158 AllowListAddressAdded(CollectionId, CrossAccountId),159160 /// Collection limits was set161 ///162 /// # Arguments163 ///164 /// * collection_id: Globally unique collection identifier.165 CollectionLimitSet(CollectionId),166167 CollectionPermissionSet(CollectionId),168169 /// Mint permission was set170 ///171 /// # Arguments172 ///173 /// * collection_id: Globally unique collection identifier.174 MintPermissionSet(CollectionId),175176 /// Offchain schema was set177 ///178 /// # Arguments179 ///180 /// * collection_id: Globally unique collection identifier.181 OffchainSchemaSet(CollectionId),182183 /// Public access mode was set184 ///185 /// # Arguments186 ///187 /// * collection_id: Globally unique collection identifier.188 ///189 /// * mode: New access state.190 PublicAccessModeSet(CollectionId, AccessMode),191192 /// Schema version was set193 ///194 /// # Arguments195 ///196 /// * collection_id: Globally unique collection identifier.197 SchemaVersionSet(CollectionId),198 }199}200201type SelfWeightOf<T> = <T as Config>::WeightInfo;202203// # Used definitions204//205// ## User control levels206//207// chain-controlled - key is uncontrolled by user208// i.e autoincrementing index209// can use non-cryptographic hash210// real - key is controlled by user211// but it is hard to generate enough colliding values, i.e owner of signed txs212// can use non-cryptographic hash213// controlled - key is completly controlled by users214// i.e maps with mutable keys215// should use cryptographic hash216//217// ## User control level downgrade reasons218//219// ?1 - chain-controlled -> controlled220// collections/tokens can be destroyed, resulting in massive holes221// ?2 - chain-controlled -> controlled222// same as ?1, but can be only added, resulting in easier exploitation223// ?3 - real -> controlled224// no confirmation required, so addresses can be easily generated225decl_storage! {226 trait Store for Module<T: Config> as Unique {227228 //#region Private members229 /// Used for migrations230 ChainVersion: u64;231 //#endregion232233 //#region Tokens transfer rate limit baskets234 /// (Collection id (controlled?2), who created (real))235 /// TODO: Off chain worker should remove from this map when collection gets removed236 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;237 /// Collection id (controlled?2), token id (controlled?2)238 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;239 /// Collection id (controlled?2), owning user (real)240 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;241 /// Collection id (controlled?2), token id (controlled?2)242 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>;243 //#endregion244245 /// Variable metadata sponsoring246 /// Collection id (controlled?2), token id (controlled?2)247 #[deprecated]248 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;249 pub TokenPropertyBasket get(fn token_property_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;250251 /// Approval sponsoring252 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;253 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;254 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>;255 }256}257258decl_module! {259 pub struct Module<T: Config> for enum Call260 where261 origin: T::Origin262 {263 type Error = Error<T>;264265 fn deposit_event() = default;266267 fn on_initialize(_now: T::BlockNumber) -> Weight {268 0269 }270271 fn on_runtime_upgrade() -> Weight {272 let limit = None;273274 <VariableMetaDataBasket<T>>::remove_all(limit);275276 0277 }278279 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.280 ///281 /// # Permissions282 ///283 /// * Anyone.284 ///285 /// # Arguments286 ///287 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.288 ///289 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.290 ///291 /// * token_prefix: UTF-8 string with token prefix.292 ///293 /// * mode: [CollectionMode] collection type and type dependent data.294 // returns collection ID295 #[weight = <SelfWeightOf<T>>::create_collection()]296 #[transactional]297 #[deprecated]298 pub fn create_collection(origin,299 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,300 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,301 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,302 mode: CollectionMode) -> DispatchResult {303 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {304 name: collection_name,305 description: collection_description,306 token_prefix,307 mode,308 ..Default::default()309 };310 Self::create_collection_ex(origin, data)311 }312313 /// This method creates a collection314 ///315 /// Prefer it to deprecated [`created_collection`] method316 #[weight = <SelfWeightOf<T>>::create_collection()]317 #[transactional]318 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {319 let sender = ensure_signed(origin)?;320321 // =========322323 T::CollectionDispatch::create(sender, data)?;324325 Ok(())326 }327328 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.329 ///330 /// # Permissions331 ///332 /// * Collection Owner.333 ///334 /// # Arguments335 ///336 /// * collection_id: collection to destroy.337 #[weight = <SelfWeightOf<T>>::destroy_collection()]338 #[transactional]339 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {340 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);341 let collection = <CollectionHandle<T>>::try_get(collection_id)?;342343 // =========344345 T::CollectionDispatch::destroy(sender, collection)?;346347 <NftTransferBasket<T>>::remove_prefix(collection_id, None);348 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);349 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);350351 <NftApproveBasket<T>>::remove_prefix(collection_id, None);352 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);353 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);354355 Ok(())356 }357358 /// Add an address to allow list.359 ///360 /// # Permissions361 ///362 /// * Collection Owner363 /// * Collection Admin364 ///365 /// # Arguments366 ///367 /// * collection_id.368 ///369 /// * address.370 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]371 #[transactional]372 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{373374 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);375 let collection = <CollectionHandle<T>>::try_get(collection_id)?;376377 <PalletCommon<T>>::toggle_allowlist(378 &collection,379 &sender,380 &address,381 true,382 )?;383384 Self::deposit_event(Event::<T>::AllowListAddressAdded(385 collection_id,386 address387 ));388389 Ok(())390 }391392 /// Remove an address from allow list.393 ///394 /// # Permissions395 ///396 /// * Collection Owner397 /// * Collection Admin398 ///399 /// # Arguments400 ///401 /// * collection_id.402 ///403 /// * address.404 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]405 #[transactional]406 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{407408 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);409 let collection = <CollectionHandle<T>>::try_get(collection_id)?;410411 <PalletCommon<T>>::toggle_allowlist(412 &collection,413 &sender,414 &address,415 false,416 )?;417418 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(419 collection_id,420 address421 ));422423 Ok(())424 }425426 /// Change the owner of the collection.427 ///428 /// # Permissions429 ///430 /// * Collection Owner.431 ///432 /// # Arguments433 ///434 /// * collection_id.435 ///436 /// * new_owner.437 #[weight = <SelfWeightOf<T>>::change_collection_owner()]438 #[transactional]439 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {440441 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);442443 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;444 target_collection.check_is_owner(&sender)?;445446 target_collection.owner = new_owner.clone();447 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(448 collection_id,449 new_owner450 ));451452 target_collection.save()453 }454455 /// Adds an admin of the Collection.456 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.457 ///458 /// # Permissions459 ///460 /// * Collection Owner.461 /// * Collection Admin.462 ///463 /// # Arguments464 ///465 /// * collection_id: ID of the Collection to add admin for.466 ///467 /// * new_admin_id: Address of new admin to add.468 #[weight = <SelfWeightOf<T>>::add_collection_admin()]469 #[transactional]470 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {471 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);472 let collection = <CollectionHandle<T>>::try_get(collection_id)?;473474 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(475 collection_id,476 new_admin_id.clone()477 ));478479 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)480 }481482 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.483 ///484 /// # Permissions485 ///486 /// * Collection Owner.487 /// * Collection Admin.488 ///489 /// # Arguments490 ///491 /// * collection_id: ID of the Collection to remove admin for.492 ///493 /// * account_id: Address of admin to remove.494 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]495 #[transactional]496 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {497 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);498 let collection = <CollectionHandle<T>>::try_get(collection_id)?;499500 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(501 collection_id,502 account_id.clone()503 ));504505 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)506 }507508 /// # Permissions509 ///510 /// * Collection Owner511 ///512 /// # Arguments513 ///514 /// * collection_id.515 ///516 /// * new_sponsor.517 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]518 #[transactional]519 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {520 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);521522 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;523 target_collection.check_is_owner(&sender)?;524525 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());526527 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(528 collection_id,529 new_sponsor530 ));531532 target_collection.save()533 }534535 /// # Permissions536 ///537 /// * Sponsor.538 ///539 /// # Arguments540 ///541 /// * collection_id.542 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]543 #[transactional]544 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {545 let sender = ensure_signed(origin)?;546547 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;548 ensure!(549 target_collection.sponsorship.pending_sponsor() == Some(&sender),550 Error::<T>::ConfirmUnsetSponsorFail551 );552553 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());554555 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(556 collection_id,557 sender558 ));559560 target_collection.save()561 }562563 /// Switch back to pay-per-own-transaction model.564 ///565 /// # Permissions566 ///567 /// * Collection owner.568 ///569 /// # Arguments570 ///571 /// * collection_id.572 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]573 #[transactional]574 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {575 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);576577 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;578 target_collection.check_is_owner(&sender)?;579580 target_collection.sponsorship = SponsorshipState::Disabled;581582 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(583 collection_id584 ));585 target_collection.save()586 }587588 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.589 ///590 /// # Permissions591 ///592 /// * Collection Owner.593 /// * Collection Admin.594 /// * Anyone if595 /// * Allow List is enabled, and596 /// * Address is added to allow list, and597 /// * MintPermission is enabled (see SetMintPermission method)598 ///599 /// # Arguments600 ///601 /// * collection_id: ID of the collection.602 ///603 /// * owner: Address, initial owner of the NFT.604 ///605 /// * data: Token data to store on chain.606 #[weight = T::CommonWeightInfo::create_item()]607 #[transactional]608 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {609 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);610 let budget = budget::Value::new(NESTING_BUDGET);611612 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))613 }614615 /// This method creates multiple items in a collection created with CreateCollection method.616 ///617 /// # Permissions618 ///619 /// * Collection Owner.620 /// * Collection Admin.621 /// * Anyone if622 /// * Allow List is enabled, and623 /// * Address is added to allow list, and624 /// * MintPermission is enabled (see SetMintPermission method)625 ///626 /// # Arguments627 ///628 /// * collection_id: ID of the collection.629 ///630 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].631 ///632 /// * owner: Address, initial owner of the NFT.633 #[weight = T::CommonWeightInfo::create_multiple_items(&items_data)]634 #[transactional]635 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {636 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);637 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);638 let budget = budget::Value::new(NESTING_BUDGET);639640 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))641 }642643 #[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]644 #[transactional]645 pub fn set_collection_properties(646 origin,647 collection_id: CollectionId,648 properties: Vec<Property>649 ) -> DispatchResultWithPostInfo {650 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);651652 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);653654 dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))655 }656657 #[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]658 #[transactional]659 pub fn delete_collection_properties(660 origin,661 collection_id: CollectionId,662 property_keys: Vec<PropertyKey>,663 ) -> DispatchResultWithPostInfo {664 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);665666 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);667668 dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))669 }670671 #[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]672 #[transactional]673 pub fn set_token_properties(674 origin,675 collection_id: CollectionId,676 token_id: TokenId,677 properties: Vec<Property>678 ) -> DispatchResultWithPostInfo {679 ensure!(!properties.is_empty(), Error::<T>::EmptyArgument);680681 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);682683 dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))684 }685686 #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]687 #[transactional]688 pub fn delete_token_properties(689 origin,690 collection_id: CollectionId,691 token_id: TokenId,692 property_keys: Vec<PropertyKey>693 ) -> DispatchResultWithPostInfo {694 ensure!(!property_keys.is_empty(), Error::<T>::EmptyArgument);695696 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);697698 dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))699 }700701 #[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]702 #[transactional]703 pub fn set_property_permissions(704 origin,705 collection_id: CollectionId,706 property_permissions: Vec<PropertyKeyPermission>,707 ) -> DispatchResultWithPostInfo {708 ensure!(!property_permissions.is_empty(), Error::<T>::EmptyArgument);709710 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);711712 dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))713 }714715 #[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]716 #[transactional]717 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {718 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);719 let budget = budget::Value::new(NESTING_BUDGET);720721 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))722 }723724 // TODO! transaction weight725726 /// Set transfers_enabled value for particular collection727 ///728 /// # Permissions729 ///730 /// * Collection Owner.731 ///732 /// # Arguments733 ///734 /// * collection_id: ID of the collection.735 ///736 /// * value: New flag value.737 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]738 #[transactional]739 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {740 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);741 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;742 target_collection.check_is_owner(&sender)?;743744 // =========745746 target_collection.limits.transfers_enabled = Some(value);747 target_collection.save()748 }749750 /// Destroys a concrete instance of NFT.751 ///752 /// # Permissions753 ///754 /// * Collection Owner.755 /// * Collection Admin.756 /// * Current NFT Owner.757 ///758 /// # Arguments759 ///760 /// * collection_id: ID of the collection.761 ///762 /// * item_id: ID of NFT to burn.763 #[weight = T::CommonWeightInfo::burn_item()]764 #[transactional]765 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {766 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);767768 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;769 if value == 1 {770 <NftTransferBasket<T>>::remove(collection_id, item_id);771 <NftApproveBasket<T>>::remove(collection_id, item_id);772 }773 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?774 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());775 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));776 Ok(post_info)777 }778779 /// Destroys a concrete instance of NFT on behalf of the owner780 /// See also: [`approve`]781 ///782 /// # Permissions783 ///784 /// * Collection Owner.785 /// * Collection Admin.786 /// * Current NFT Owner.787 ///788 /// # Arguments789 ///790 /// * collection_id: ID of the collection.791 ///792 /// * item_id: ID of NFT to burn.793 ///794 /// * from: owner of item795 #[weight = T::CommonWeightInfo::burn_from()]796 #[transactional]797 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {798 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);799 let budget = budget::Value::new(NESTING_BUDGET);800801 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))802 }803804 /// Change ownership of the token.805 ///806 /// # Permissions807 ///808 /// * Collection Owner809 /// * Collection Admin810 /// * Current NFT owner811 ///812 /// # Arguments813 ///814 /// * recipient: Address of token recipient.815 ///816 /// * collection_id.817 ///818 /// * item_id: ID of the item819 /// * Non-Fungible Mode: Required.820 /// * Fungible Mode: Ignored.821 /// * Re-Fungible Mode: Required.822 ///823 /// * value: Amount to transfer.824 /// * Non-Fungible Mode: Ignored825 /// * Fungible Mode: Must specify transferred amount826 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)827 #[weight = T::CommonWeightInfo::transfer()]828 #[transactional]829 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {830 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);831 let budget = budget::Value::new(NESTING_BUDGET);832833 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))834 }835836 /// Set, change, or remove approved address to transfer the ownership of the NFT.837 ///838 /// # Permissions839 ///840 /// * Collection Owner841 /// * Collection Admin842 /// * Current NFT owner843 ///844 /// # Arguments845 ///846 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).847 ///848 /// * collection_id.849 ///850 /// * item_id: ID of the item.851 #[weight = T::CommonWeightInfo::approve()]852 #[transactional]853 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {854 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);855856 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))857 }858859 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.860 ///861 /// # Permissions862 /// * Collection Owner863 /// * Collection Admin864 /// * Current NFT owner865 /// * Address approved by current NFT owner866 ///867 /// # Arguments868 ///869 /// * from: Address that owns token.870 ///871 /// * recipient: Address of token recipient.872 ///873 /// * collection_id.874 ///875 /// * item_id: ID of the item.876 ///877 /// * value: Amount to transfer.878 #[weight = T::CommonWeightInfo::transfer_from()]879 #[transactional]880 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {881 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);882 let budget = budget::Value::new(NESTING_BUDGET);883884 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))885 }886887 #[weight = <SelfWeightOf<T>>::set_collection_limits()]888 #[transactional]889 pub fn set_collection_limits(890 origin,891 collection_id: CollectionId,892 new_limit: CollectionLimits,893 ) -> DispatchResult {894 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;896 target_collection.check_is_owner(&sender)?;897 let old_limit = &target_collection.limits;898899 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;900901 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(902 collection_id903 ));904905 target_collection.save()906 }907908 #[weight = <SelfWeightOf<T>>::set_collection_limits()]909 #[transactional]910 pub fn set_collection_permissions(911 origin,912 collection_id: CollectionId,913 new_limit: CollectionPermissions,914 ) -> DispatchResult {915 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);916 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;917 target_collection.check_is_owner(&sender)?;918 let old_limit = &target_collection.permissions;919920 target_collection.permissions = <PalletCommon<T>>::clamp_permissions(target_collection.mode.clone(), &old_limit, new_limit)?;921922 <Pallet<T>>::deposit_event(Event::<T>::CollectionPermissionSet(923 collection_id924 ));925926 target_collection.save()927 }928 }929}