difftreelog
feat external-internal api collection creation segreation
in: master
11 files changed
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -36,6 +36,13 @@
},
error,
})?;
+ handle.check_is_internal().map_err(|error| DispatchErrorWithPostInfo {
+ post_info: PostDispatchInfo {
+ actual_weight: Some(dispatch_weight::<T>()),
+ pays_fee: Pays::Yes,
+ },
+ error,
+ })?;
let dispatched = T::CollectionDispatch::dispatch(handle);
let mut result = call(dispatched.as_dyn());
match &mut result {
pallets/common/src/erc.rsdiffbeforeafterboth--- a/pallets/common/src/erc.rs
+++ b/pallets/common/src/erc.rs
@@ -316,8 +316,9 @@
}
fn save<T: Config>(collection: &CollectionHandle<T>) -> Result<void> {
+ // TODO possibly delete for the lack of transaction
collection
- .check_is_mutable()
+ .check_is_internal()
.map_err(dispatch_to_evm::<T>)?;
<crate::CollectionById<T>>::insert(collection.id, collection.collection.clone());
Ok(())
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -149,20 +149,16 @@
))
}
pub fn save(self) -> Result<(), DispatchError> {
- self.check_is_mutable()?;
<CollectionById<T>>::insert(self.id, self.collection);
Ok(())
}
pub fn set_sponsor(&mut self, sponsor: T::AccountId) -> DispatchResult {
- self.check_is_mutable()?;
self.collection.sponsorship = SponsorshipState::Unconfirmed(sponsor);
Ok(())
}
pub fn confirm_sponsorship(&mut self, sender: &T::AccountId) -> Result<bool, DispatchError> {
- self.check_is_mutable()?;
-
if self.collection.sponsorship.pending_sponsor() != Some(sender) {
return Ok(false);
}
@@ -171,11 +167,21 @@
Ok(true)
}
- /// Checks that collection is can be mutate.
- /// Now check only `external_collection` flag and if it **true**, than return `CollectionIsReadOnly` error.
- pub fn check_is_mutable(&self) -> DispatchResult {
+ /// Checks that the collection was created with, and must be operated upon through **Unique API**.
+ /// Now check only the `external_collection` flag and if it's **true**, then return `CollectionIsExternal` error.
+ pub fn check_is_internal(&self) -> DispatchResult {
if self.external_collection {
- return Err(<Error<T>>::CollectionIsReadOnly)?;
+ return Err(<Error<T>>::CollectionIsExternal)?;
+ }
+
+ Ok(())
+ }
+
+ /// Checks that the collection was created with, and must be operated upon through an **assimilated API**.
+ /// Now check only the `external_collection` flag and if it's **false**, then return `CollectionIsInternal` error.
+ pub fn check_is_external(&self) -> DispatchResult {
+ if !self.external_collection {
+ return Err(<Error<T>>::CollectionIsInternal)?;
}
Ok(())
@@ -449,8 +455,11 @@
/// Empty property keys are forbidden
EmptyPropertyKey,
- /// Collection is read only
- CollectionIsReadOnly,
+ /// Tried to access an external collection with an internal API
+ CollectionIsExternal,
+
+ /// Tried to access an internal collection with an external API
+ CollectionIsInternal,
}
#[pallet::storage]
@@ -754,6 +763,7 @@
pub fn init_collection(
owner: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ is_external: bool,
) -> Result<CollectionId, DispatchError> {
{
ensure!(
@@ -797,7 +807,7 @@
Self::clamp_permissions(data.mode.clone(), &Default::default(), permissions)
})
.unwrap_or_else(|| Ok(CollectionPermissions::default()))?,
- external_collection: false,
+ external_collection: is_external,
};
let mut collection_properties = up_data_structs::CollectionProperties::get();
@@ -854,7 +864,6 @@
collection: CollectionHandle<T>,
sender: &T::CrossAccountId,
) -> DispatchResult {
- collection.check_is_mutable()?;
ensure!(
collection.limits.owner_can_destroy(),
<Error<T>>::NoPermission,
@@ -884,7 +893,6 @@
sender: &T::CrossAccountId,
property: Property,
) -> DispatchResult {
- collection.check_is_mutable()?;
collection.check_is_owner_or_admin(sender)?;
CollectionProperties::<T>::try_mutate(collection.id, |properties| {
@@ -930,8 +938,6 @@
sender: &T::CrossAccountId,
properties: Vec<Property>,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
for property in properties {
Self::set_collection_property(collection, sender, property)?;
}
@@ -944,7 +950,6 @@
sender: &T::CrossAccountId,
property_key: PropertyKey,
) -> DispatchResult {
- collection.check_is_mutable()?;
collection.check_is_owner_or_admin(sender)?;
CollectionProperties::<T>::try_mutate(collection.id, |properties| {
@@ -966,8 +971,6 @@
sender: &T::CrossAccountId,
property_keys: Vec<PropertyKey>,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
for key in property_keys {
Self::delete_collection_property(collection, sender, key)?;
}
@@ -992,7 +995,6 @@
sender: &T::CrossAccountId,
property_permission: PropertyKeyPermission,
) -> DispatchResult {
- collection.check_is_mutable()?;
collection.check_is_owner_or_admin(sender)?;
let all_permissions = CollectionPropertyPermissions::<T>::get(collection.id);
@@ -1024,8 +1026,6 @@
sender: &T::CrossAccountId,
property_permissions: Vec<PropertyKeyPermission>,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
for prop_pemission in property_permissions {
Self::set_property_permission(collection, sender, prop_pemission)?;
}
@@ -1113,7 +1113,6 @@
user: &T::CrossAccountId,
allowed: bool,
) -> DispatchResult {
- collection.check_is_mutable()?;
collection.check_is_owner_or_admin(sender)?;
// =========
@@ -1133,7 +1132,6 @@
user: &T::CrossAccountId,
admin: bool,
) -> DispatchResult {
- collection.check_is_mutable()?;
collection.check_is_owner_or_admin(sender)?;
let was_admin = <IsAdmin<T>>::get((collection.id, user));
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -137,7 +137,7 @@
owner: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, data)
+ <PalletCommon<T>>::init_collection(owner, data, false)
}
pub fn destroy_collection(
collection: FungibleHandle<T>,
@@ -168,8 +168,6 @@
owner: &T::CrossAccountId,
amount: u128,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
let total_supply = <TotalSupply<T>>::get(collection.id)
.checked_sub(amount)
.ok_or(<CommonError<T>>::TokenValueTooLow)?;
@@ -216,8 +214,6 @@
amount: u128,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed,
@@ -287,8 +283,6 @@
data: BTreeMap<T::CrossAccountId, u128>,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
if !collection.is_owner_or_admin(sender) {
ensure!(
collection.permissions.mint_mode(),
@@ -390,7 +384,6 @@
spender: &T::CrossAccountId,
amount: u128,
) -> DispatchResult {
- collection.check_is_mutable()?;
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(owner)?;
collection.check_allowlist(spender)?;
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -304,8 +304,9 @@
pub fn init_collection(
owner: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
+ is_external: bool,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, data)
+ <PalletCommon<T>>::init_collection(owner, data, is_external)
}
pub fn destroy_collection(
collection: NonfungibleHandle<T>,
@@ -336,8 +337,6 @@
sender: &T::CrossAccountId,
token: TokenId,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
let token_data =
<TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
ensure!(
@@ -458,7 +457,6 @@
&property.key,
is_token_create,
)?;
- collection.check_is_mutable()?;
<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
let property = property.clone();
@@ -496,7 +494,6 @@
token_id: TokenId,
property_key: PropertyKey,
) -> DispatchResult {
- collection.check_is_mutable()?;
Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?;
<TokenProperties<T>>::try_mutate((collection.id, token_id), |properties| {
@@ -574,8 +571,6 @@
token_id: TokenId,
property_keys: Vec<PropertyKey>,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
for key in property_keys {
Self::delete_token_property(collection, sender, token_id, key)?;
}
@@ -622,8 +617,6 @@
token: TokenId,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
@@ -902,8 +895,6 @@
token: TokenId,
spender: Option<&T::CrossAccountId>,
) -> DispatchResult {
- collection.check_is_mutable()?;
-
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
if let Some(spender) = spender {
pallets/proxy-rmrk-core/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#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::{*, mapping::TokenAddressMapping};24use pallet_common::{25 Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::{Pallet as PalletStructure, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46 use super::*;47 use pallet_evm::account;4849 #[pallet::config]50 pub trait Config:51 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52 {53 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54 }5556 #[pallet::storage]57 #[pallet::getter(fn collection_index)]58 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960 #[pallet::storage]61 pub type UniqueCollectionId<T: Config> =62 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364 #[pallet::storage]65 pub type RmrkInernalCollectionId<T: Config> =66 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768 #[pallet::pallet]69 #[pallet::generate_store(pub(super) trait Store)]70 pub struct Pallet<T>(_);7172 #[pallet::event]73 #[pallet::generate_deposit(pub(super) fn deposit_event)]74 pub enum Event<T: Config> {75 CollectionCreated {76 issuer: T::AccountId,77 collection_id: RmrkCollectionId,78 },79 CollectionDestroyed {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 IssuerChanged {84 old_issuer: T::AccountId,85 new_issuer: T::AccountId,86 collection_id: RmrkCollectionId,87 },88 CollectionLocked {89 issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 NftMinted {93 owner: T::AccountId,94 collection_id: RmrkCollectionId,95 nft_id: RmrkNftId,96 },97 NFTBurned {98 owner: T::AccountId,99 nft_id: RmrkNftId,100 },101 NFTSent {102 sender: T::AccountId,103 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,104 collection_id: RmrkCollectionId,105 nft_id: RmrkNftId,106 approval_required: bool,107 },108 NFTAccepted {109 sender: T::AccountId,110 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111 collection_id: RmrkCollectionId,112 nft_id: RmrkNftId,113 },114 NFTRejected {115 sender: T::AccountId,116 collection_id: RmrkCollectionId,117 nft_id: RmrkNftId,118 },119 PropertySet {120 collection_id: RmrkCollectionId,121 maybe_nft_id: Option<RmrkNftId>,122 key: RmrkKeyString,123 value: RmrkValueString,124 },125 ResourceAdded {126 nft_id: RmrkNftId,127 resource_id: RmrkResourceId,128 },129 ResourceRemoval {130 nft_id: RmrkNftId,131 resource_id: RmrkResourceId,132 },133 ResourceAccepted {134 nft_id: RmrkNftId,135 resource_id: RmrkResourceId,136 },137 ResourceRemovalAccepted {138 nft_id: RmrkNftId,139 resource_id: RmrkResourceId,140 },141 PrioritySet {142 collection_id: RmrkCollectionId,143 nft_id: RmrkNftId,144 },145 }146147 #[pallet::error]148 pub enum Error<T> {149 /* Unique-specific events */150 CorruptedCollectionType,151 NftTypeEncodeError,152 RmrkPropertyKeyIsTooLong,153 RmrkPropertyValueIsTooLong,154155 /* RMRK compatible events */156 CollectionNotEmpty,157 NoAvailableCollectionId,158 NoAvailableNftId,159 CollectionUnknown,160 NoPermission,161 NonTransferable,162 CollectionFullOrLocked,163 ResourceDoesntExist,164 CannotSendToDescendentOrSelf,165 CannotAcceptNonOwnedNft,166 CannotRejectNonOwnedNft,167 ResourceNotPending,168 }169170 #[pallet::call]171 impl<T: Config> Pallet<T> {172 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]173 #[transactional]174 pub fn create_collection(175 origin: OriginFor<T>,176 metadata: RmrkString,177 max: Option<u32>,178 symbol: RmrkCollectionSymbol,179 ) -> DispatchResult {180 let sender = ensure_signed(origin)?;181182 let limits = CollectionLimits {183 owner_can_transfer: Some(false),184 token_limit: max,185 ..Default::default()186 };187188 let data = CreateCollectionData {189 limits: Some(limits),190 token_prefix: symbol191 .into_inner()192 .try_into()193 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,194 permissions: Some(CollectionPermissions {195 nesting: Some(NestingRule::Owner),196 ..Default::default()197 }),198 ..Default::default()199 };200201 let unique_collection_id = Self::init_collection(202 T::CrossAccountId::from_sub(sender.clone()),203 data,204 [205 Self::rmrk_property(Metadata, &metadata)?,206 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,207 ]208 .into_iter(),209 )?;210 let rmrk_collection_id = <CollectionIndex<T>>::get();211212 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);213 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);214215 <CollectionIndex<T>>::mutate(|n| *n += 1);216217 Self::deposit_event(Event::CollectionCreated {218 issuer: sender,219 collection_id: rmrk_collection_id,220 });221222 Ok(())223 }224225 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]226 #[transactional]227 pub fn destroy_collection(228 origin: OriginFor<T>,229 collection_id: RmrkCollectionId,230 ) -> DispatchResult {231 let sender = ensure_signed(origin)?;232 let cross_sender = T::CrossAccountId::from_sub(sender.clone());233234 let collection = Self::get_typed_nft_collection(235 Self::unique_collection_id(collection_id)?,236 misc::CollectionType::Regular,237 )?;238239 <PalletNft<T>>::destroy_collection(collection, &cross_sender)240 .map_err(Self::map_unique_err_to_proxy)?;241242 Self::deposit_event(Event::CollectionDestroyed {243 issuer: sender,244 collection_id,245 });246247 Ok(())248 }249250 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]251 #[transactional]252 pub fn change_collection_issuer(253 origin: OriginFor<T>,254 collection_id: RmrkCollectionId,255 new_issuer: <T::Lookup as StaticLookup>::Source,256 ) -> DispatchResult {257 let sender = ensure_signed(origin)?;258259 let new_issuer = T::Lookup::lookup(new_issuer)?;260261 Self::change_collection_owner(262 Self::unique_collection_id(collection_id)?,263 misc::CollectionType::Regular,264 sender.clone(),265 new_issuer.clone(),266 )?;267268 Self::deposit_event(Event::IssuerChanged {269 old_issuer: sender,270 new_issuer,271 collection_id,272 });273274 Ok(())275 }276277 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]278 #[transactional]279 pub fn lock_collection(280 origin: OriginFor<T>,281 collection_id: RmrkCollectionId,282 ) -> DispatchResult {283 let sender = ensure_signed(origin)?;284 let cross_sender = T::CrossAccountId::from_sub(sender.clone());285286 let collection = Self::get_typed_nft_collection(287 Self::unique_collection_id(collection_id)?,288 misc::CollectionType::Regular,289 )?;290291 Self::check_collection_owner(&collection, &cross_sender)?;292293 let token_count = collection.total_supply();294295 let mut collection = collection.into_inner();296 collection.limits.token_limit = Some(token_count);297 collection.save()?;298299 Self::deposit_event(Event::CollectionLocked {300 issuer: sender,301 collection_id,302 });303304 Ok(())305 }306307 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]308 #[transactional]309 pub fn mint_nft(310 origin: OriginFor<T>,311 owner: T::AccountId,312 collection_id: RmrkCollectionId,313 recipient: Option<T::AccountId>,314 royalty_amount: Option<Permill>,315 metadata: RmrkString,316 transferable: bool,317 ) -> DispatchResult {318 let sender = ensure_signed(origin)?;319 let sender = T::CrossAccountId::from_sub(sender);320 let cross_owner = T::CrossAccountId::from_sub(owner.clone());321322 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {323 recipient: recipient.unwrap_or_else(|| owner.clone()),324 amount,325 });326327 let collection = Self::get_typed_nft_collection(328 Self::unique_collection_id(collection_id)?,329 misc::CollectionType::Regular,330 )?;331332 let nft_id = Self::create_nft(333 &sender,334 &cross_owner,335 &collection,336 [337 Self::rmrk_property(TokenType, &NftType::Regular)?,338 Self::rmrk_property(Transferable, &transferable)?,339 Self::rmrk_property(PendingNftAccept, &false)?,340 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,341 Self::rmrk_property(Metadata, &metadata)?,342 Self::rmrk_property(Equipped, &false)?,343 Self::rmrk_property(344 ResourceCollection,345 &Self::init_collection(346 sender.clone(),347 CreateCollectionData {348 ..Default::default()349 },350 [Self::rmrk_property(351 CollectionType,352 &misc::CollectionType::Resource,353 )?]354 .into_iter(),355 )?,356 )?, // todo possibly add limits to the collection if rmrk warrants them357 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,358 ]359 .into_iter(),360 )361 .map_err(|err| match err {362 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),363 err => Self::map_unique_err_to_proxy(err),364 })?;365366 Self::deposit_event(Event::NftMinted {367 owner,368 collection_id,369 nft_id: nft_id.0,370 });371372 Ok(())373 }374375 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]376 #[transactional]377 pub fn burn_nft(378 origin: OriginFor<T>,379 collection_id: RmrkCollectionId,380 nft_id: RmrkNftId,381 ) -> DispatchResult {382 let sender = ensure_signed(origin)?;383 let cross_sender = T::CrossAccountId::from_sub(sender.clone());384385 Self::destroy_nft(386 cross_sender,387 Self::unique_collection_id(collection_id)?,388 nft_id.into(),389 )390 .map_err(Self::map_unique_err_to_proxy)?;391392 Self::deposit_event(Event::NFTBurned {393 owner: sender,394 nft_id,395 });396397 Ok(())398 }399400 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]401 #[transactional]402 pub fn send(403 origin: OriginFor<T>,404 rmrk_collection_id: RmrkCollectionId,405 rmrk_nft_id: RmrkNftId,406 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,407 ) -> DispatchResult {408 let sender = ensure_signed(origin.clone())?;409 let cross_sender = T::CrossAccountId::from_sub(sender.clone());410411 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;412 let nft_id = rmrk_nft_id.into();413414 let token_data =415 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;416417 let from = token_data.owner;418419 let collection =420 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;421422 ensure!(423 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,424 <Error<T>>::NonTransferable425 );426427 ensure!(428 !Self::get_nft_property_decoded(429 collection_id,430 nft_id,431 RmrkProperty::PendingNftAccept432 )?,433 <Error<T>>::NoPermission434 );435436 let target_owner;437 let approval_required;438439 match new_owner {440 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {441 target_owner = T::CrossAccountId::from_sub(account_id.clone());442 approval_required = false;443 }444 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(445 target_collection_id,446 target_nft_id,447 ) => {448 let target_collection_id = Self::unique_collection_id(target_collection_id)?;449450 let target_nft_budget = budget::Value::new(NESTING_BUDGET);451452 let target_nft_owner = <PalletStructure<T>>::get_checked_indirect_owner(453 target_collection_id,454 target_nft_id.into(),455 Some((collection_id, nft_id)),456 &target_nft_budget,457 )458 .map_err(Self::map_unique_err_to_proxy)?;459460 approval_required = cross_sender != target_nft_owner;461462 if approval_required {463 target_owner = target_nft_owner;464465 <PalletNft<T>>::set_scoped_token_property(466 collection.id,467 nft_id,468 PropertyScope::Rmrk,469 Self::rmrk_property(PendingNftAccept, &approval_required)?,470 )?;471 } else {472 target_owner = T::CrossTokenAddressMapping::token_to_address(473 target_collection_id,474 target_nft_id.into(),475 );476 }477 }478 }479480 let src_nft_budget = budget::Value::new(NESTING_BUDGET);481482 <PalletNft<T>>::transfer_from(483 &collection,484 &cross_sender,485 &from,486 &target_owner,487 nft_id,488 &src_nft_budget,489 )490 .map_err(Self::map_unique_err_to_proxy)?;491492 Self::deposit_event(Event::NFTSent {493 sender,494 recipient: new_owner,495 collection_id: rmrk_collection_id,496 nft_id: rmrk_nft_id,497 approval_required,498 });499500 Ok(())501 }502503 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]504 #[transactional]505 pub fn accept_nft(506 origin: OriginFor<T>,507 rmrk_collection_id: RmrkCollectionId,508 rmrk_nft_id: RmrkNftId,509 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,510 ) -> DispatchResult {511 let sender = ensure_signed(origin.clone())?;512 let cross_sender = T::CrossAccountId::from_sub(sender.clone());513514 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;515 let nft_id = rmrk_nft_id.into();516517 let collection =518 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;519520 let new_cross_owner = match new_owner {521 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {522 T::CrossAccountId::from_sub(account_id.clone())523 }524 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(525 target_collection_id,526 target_nft_id,527 ) => {528 let target_collection_id = Self::unique_collection_id(target_collection_id)?;529530 T::CrossTokenAddressMapping::token_to_address(531 target_collection_id,532 TokenId(target_nft_id),533 )534 }535 };536537 let budget = budget::Value::new(NESTING_BUDGET);538539 <PalletNft<T>>::transfer(540 &collection,541 &cross_sender,542 &new_cross_owner,543 nft_id,544 &budget,545 )546 .map_err(|err| {547 if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {548 <Error<T>>::CannotAcceptNonOwnedNft.into()549 } else {550 Self::map_unique_err_to_proxy(err)551 }552 })?;553554 <PalletNft<T>>::set_scoped_token_property(555 collection.id,556 nft_id,557 PropertyScope::Rmrk,558 Self::rmrk_property(PendingNftAccept, &false)?,559 )?;560561 Self::deposit_event(Event::NFTAccepted {562 sender,563 recipient: new_owner,564 collection_id: rmrk_collection_id,565 nft_id: rmrk_nft_id,566 });567568 Ok(())569 }570571 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]572 #[transactional]573 pub fn reject_nft(574 origin: OriginFor<T>,575 rmrk_collection_id: RmrkCollectionId,576 rmrk_nft_id: RmrkNftId,577 ) -> DispatchResult {578 let sender = ensure_signed(origin)?;579 let cross_sender = T::CrossAccountId::from_sub(sender.clone());580581 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;582 let nft_id = rmrk_nft_id.into();583584 Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {585 if err == <CommonError<T>>::NoPermission.into()586 || err == <CommonError<T>>::ApprovedValueTooLow.into()587 {588 <Error<T>>::CannotRejectNonOwnedNft.into()589 } else {590 Self::map_unique_err_to_proxy(err)591 }592 })?;593594 Self::deposit_event(Event::NFTRejected {595 sender,596 collection_id: rmrk_collection_id,597 nft_id: rmrk_nft_id,598 });599600 Ok(())601 }602603 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]604 #[transactional]605 pub fn accept_resource(606 origin: OriginFor<T>,607 rmrk_collection_id: RmrkCollectionId,608 rmrk_nft_id: RmrkNftId,609 rmrk_resource_id: RmrkResourceId,610 ) -> DispatchResult {611 let sender = ensure_signed(origin)?;612 let cross_sender = T::CrossAccountId::from_sub(sender);613614 let collection_id = Self::unique_collection_id(rmrk_collection_id)615 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;616617 let nft_id = rmrk_nft_id.into();618 let resource_id = rmrk_resource_id.into();619620 let budget = budget::Value::new(NESTING_BUDGET);621622 let nft_owner =623 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)624 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;625626 let resource_collection_id: CollectionId =627 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)628 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;629630 let is_pending: bool = Self::get_nft_property_decoded(631 resource_collection_id,632 resource_id,633 PendingResourceAccept,634 )635 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;636637 ensure!(is_pending, <Error<T>>::ResourceNotPending);638639 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);640641 <PalletNft<T>>::set_scoped_token_property(642 resource_collection_id,643 rmrk_resource_id.into(),644 PropertyScope::Rmrk,645 Self::rmrk_property(PendingResourceAccept, &false)?,646 )?;647648 Self::deposit_event(Event::<T>::ResourceAccepted {649 nft_id: rmrk_nft_id,650 resource_id: rmrk_resource_id,651 });652653 Ok(())654 }655656 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]657 #[transactional]658 pub fn accept_resource_removal(659 origin: OriginFor<T>,660 rmrk_collection_id: RmrkCollectionId,661 rmrk_nft_id: RmrkNftId,662 rmrk_resource_id: RmrkResourceId,663 ) -> DispatchResult {664 let sender = ensure_signed(origin)?;665 let cross_sender = T::CrossAccountId::from_sub(sender);666667 let collection_id = Self::unique_collection_id(rmrk_collection_id)668 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;669670 let nft_id = rmrk_nft_id.into();671 let resource_id = rmrk_resource_id.into();672673 let budget = budget::Value::new(NESTING_BUDGET);674675 let nft_owner =676 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)677 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;678679 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);680681 let resource_collection_id: CollectionId =682 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)683 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;684685 let is_pending: bool = Self::get_nft_property_decoded(686 resource_collection_id,687 resource_id,688 PendingResourceRemoval,689 )690 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;691692 ensure!(is_pending, <Error<T>>::ResourceNotPending);693694 let resource_collection = Self::get_typed_nft_collection(695 resource_collection_id,696 misc::CollectionType::Resource,697 )?;698699 <PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())700 .map_err(Self::map_unique_err_to_proxy)?;701702 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {703 nft_id: rmrk_nft_id,704 resource_id: rmrk_resource_id,705 });706707 Ok(())708 }709710 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]711 #[transactional]712 pub fn set_property(713 origin: OriginFor<T>,714 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,715 maybe_nft_id: Option<RmrkNftId>,716 key: RmrkKeyString,717 value: RmrkValueString,718 ) -> DispatchResult {719 let sender = ensure_signed(origin)?;720 let sender = T::CrossAccountId::from_sub(sender);721722 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;723 let budget = budget::Value::new(NESTING_BUDGET);724725 match maybe_nft_id {726 Some(nft_id) => {727 let token_id: TokenId = nft_id.into();728729 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;730 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;731732 <PalletNft<T>>::set_scoped_token_property(733 collection_id,734 token_id,735 PropertyScope::Rmrk,736 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,737 )?;738 }739 None => {740 let collection = Self::get_typed_nft_collection(741 collection_id,742 misc::CollectionType::Regular,743 )?;744745 Self::check_collection_owner(&collection, &sender)?;746747 <PalletCommon<T>>::set_scoped_collection_property(748 collection_id,749 PropertyScope::Rmrk,750 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,751 )?;752 }753 }754755 Self::deposit_event(Event::PropertySet {756 collection_id: rmrk_collection_id,757 maybe_nft_id,758 key,759 value,760 });761762 Ok(())763 }764765 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]766 #[transactional]767 pub fn set_priority(768 origin: OriginFor<T>,769 rmrk_collection_id: RmrkCollectionId,770 rmrk_nft_id: RmrkNftId,771 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,772 ) -> DispatchResult {773 let sender = ensure_signed(origin)?;774 let sender = T::CrossAccountId::from_sub(sender);775776 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;777 let nft_id = rmrk_nft_id.into();778 let budget = budget::Value::new(NESTING_BUDGET);779780 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;781 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;782783 <PalletNft<T>>::set_scoped_token_property(784 collection_id,785 nft_id,786 PropertyScope::Rmrk,787 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,788 )?;789790 Self::deposit_event(Event::<T>::PrioritySet {791 collection_id: rmrk_collection_id,792 nft_id: rmrk_nft_id,793 });794795 Ok(())796 }797798 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]799 #[transactional]800 pub fn add_basic_resource(801 origin: OriginFor<T>,802 collection_id: RmrkCollectionId,803 nft_id: RmrkNftId,804 resource: RmrkBasicResource,805 ) -> DispatchResult {806 let sender = ensure_signed(origin.clone())?;807808 let resource_id = Self::resource_add(809 sender,810 Self::unique_collection_id(collection_id)?,811 nft_id.into(),812 [813 Self::rmrk_property(TokenType, &NftType::Resource)?,814 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,815 Self::rmrk_property(Src, &resource.src)?,816 Self::rmrk_property(Metadata, &resource.metadata)?,817 Self::rmrk_property(License, &resource.license)?,818 Self::rmrk_property(Thumb, &resource.thumb)?,819 ]820 .into_iter(),821 )?;822823 Self::deposit_event(Event::ResourceAdded {824 nft_id,825 resource_id,826 });827 Ok(())828 }829830 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]831 #[transactional]832 pub fn add_composable_resource(833 origin: OriginFor<T>,834 collection_id: RmrkCollectionId,835 nft_id: RmrkNftId,836 _resource_id: RmrkBoundedResource,837 resource: RmrkComposableResource,838 ) -> DispatchResult {839 let sender = ensure_signed(origin.clone())?;840841 let resource_id = Self::resource_add(842 sender,843 Self::unique_collection_id(collection_id)?,844 nft_id.into(),845 [846 Self::rmrk_property(TokenType, &NftType::Resource)?,847 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,848 Self::rmrk_property(Parts, &resource.parts)?,849 Self::rmrk_property(Base, &resource.base)?,850 Self::rmrk_property(Src, &resource.src)?,851 Self::rmrk_property(Metadata, &resource.metadata)?,852 Self::rmrk_property(License, &resource.license)?,853 Self::rmrk_property(Thumb, &resource.thumb)?,854 ]855 .into_iter(),856 )?;857858 Self::deposit_event(Event::ResourceAdded {859 nft_id,860 resource_id,861 });862 Ok(())863 }864865 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]866 #[transactional]867 pub fn add_slot_resource(868 origin: OriginFor<T>,869 collection_id: RmrkCollectionId,870 nft_id: RmrkNftId,871 resource: RmrkSlotResource,872 ) -> DispatchResult {873 let sender = ensure_signed(origin.clone())?;874875 let resource_id = Self::resource_add(876 sender,877 Self::unique_collection_id(collection_id)?,878 nft_id.into(),879 [880 Self::rmrk_property(TokenType, &NftType::Resource)?,881 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,882 Self::rmrk_property(Base, &resource.base)?,883 Self::rmrk_property(Src, &resource.src)?,884 Self::rmrk_property(Metadata, &resource.metadata)?,885 Self::rmrk_property(Slot, &resource.slot)?,886 Self::rmrk_property(License, &resource.license)?,887 Self::rmrk_property(Thumb, &resource.thumb)?,888 ]889 .into_iter(),890 )?;891892 Self::deposit_event(Event::ResourceAdded {893 nft_id,894 resource_id,895 });896 Ok(())897 }898899 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]900 #[transactional]901 pub fn remove_resource(902 origin: OriginFor<T>,903 collection_id: RmrkCollectionId,904 nft_id: RmrkNftId,905 resource_id: RmrkResourceId,906 ) -> DispatchResult {907 let sender = ensure_signed(origin.clone())?;908909 Self::resource_remove(910 sender,911 Self::unique_collection_id(collection_id)?,912 nft_id.into(),913 resource_id.into(),914 )?;915916 Self::deposit_event(Event::ResourceRemoval {917 nft_id,918 resource_id,919 });920 Ok(())921 }922 }923}924925impl<T: Config> Pallet<T> {926 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {927 let key = rmrk_key.to_key::<T>()?;928929 let scoped_key = PropertyScope::Rmrk930 .apply(key)931 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;932933 Ok(scoped_key)934 }935936 // todo think about renaming these937 pub fn rmrk_property<E: Encode>(938 rmrk_key: RmrkProperty,939 value: &E,940 ) -> Result<Property, DispatchError> {941 let key = rmrk_key.to_key::<T>()?;942943 let value = value944 .encode()945 .try_into()946 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;947948 let property = Property { key, value };949950 Ok(property)951 }952953 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {954 vec.decode()955 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())956 }957958 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>959 where960 BoundedVec<u8, S>: TryFrom<Vec<u8>>,961 {962 vec.rebind()963 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())964 }965966 fn init_collection(967 sender: T::CrossAccountId,968 data: CreateCollectionData<T::AccountId>,969 properties: impl Iterator<Item = Property>,970 ) -> Result<CollectionId, DispatchError> {971 let collection_id = <PalletNft<T>>::init_collection(sender, data);972973 if let Err(DispatchError::Arithmetic(_)) = &collection_id {974 return Err(<Error<T>>::NoAvailableCollectionId.into());975 }976977 <PalletCommon<T>>::set_scoped_collection_properties(978 collection_id?,979 PropertyScope::Rmrk,980 properties,981 )?;982983 collection_id984 }985986 pub fn create_nft(987 sender: &T::CrossAccountId,988 owner: &T::CrossAccountId,989 collection: &NonfungibleHandle<T>,990 properties: impl Iterator<Item = Property>,991 ) -> Result<TokenId, DispatchError> {992 let data = CreateNftExData {993 properties: BoundedVec::default(),994 owner: owner.clone(),995 };996997 let budget = budget::Value::new(NESTING_BUDGET);998999 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10001001 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10021003 <PalletNft<T>>::set_scoped_token_properties(1004 collection.id,1005 nft_id,1006 PropertyScope::Rmrk,1007 properties,1008 )?;10091010 Ok(nft_id)1011 }10121013 fn destroy_nft(1014 sender: T::CrossAccountId,1015 collection_id: CollectionId,1016 token_id: TokenId,1017 ) -> DispatchResult {1018 let collection =1019 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10201021 let token_data =1022 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10231024 let from = token_data.owner;10251026 let budget = budget::Value::new(NESTING_BUDGET);10271028 <PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1029 }10301031 fn resource_add(1032 sender: T::AccountId,1033 collection_id: CollectionId,1034 token_id: TokenId,1035 resource_properties: impl Iterator<Item = Property>,1036 ) -> Result<RmrkResourceId, DispatchError> {1037 let collection =1038 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1039 ensure!(collection.owner == sender, Error::<T>::NoPermission);10401041 let sender = T::CrossAccountId::from_sub(sender);1042 let budget = budget::Value::new(NESTING_BUDGET);10431044 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1045 .map_err(Self::map_unique_err_to_proxy)?;10461047 let pending = sender != nft_owner;10481049 let resource_collection_id: CollectionId =1050 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1051 let resource_collection =1052 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;10531054 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them10551056 let resource_id = Self::create_nft(1057 &sender,1058 &nft_owner,1059 &resource_collection,1060 resource_properties.chain(1061 [1062 Self::rmrk_property(PendingResourceAccept, &pending)?,1063 Self::rmrk_property(PendingResourceRemoval, &false)?,1064 ]1065 .into_iter(),1066 ),1067 )1068 .map_err(|err| match err {1069 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1070 err => Self::map_unique_err_to_proxy(err),1071 })?;10721073 Ok(resource_id.0)1074 }10751076 fn resource_remove(1077 sender: T::AccountId,1078 collection_id: CollectionId,1079 nft_id: TokenId,1080 resource_id: TokenId,1081 ) -> DispatchResult {1082 let collection =1083 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1084 ensure!(collection.owner == sender, Error::<T>::NoPermission);10851086 let resource_collection_id: CollectionId =1087 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1088 let resource_collection =1089 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1090 ensure!(1091 <PalletNft<T>>::token_exists(&resource_collection, resource_id),1092 Error::<T>::ResourceDoesntExist1093 );10941095 let budget = up_data_structs::budget::Value::new(10);1096 let topmost_owner =1097 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;10981099 let sender = T::CrossAccountId::from_sub(sender);1100 if topmost_owner == sender {1101 <PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1102 .map_err(Self::map_unique_err_to_proxy)?;1103 } else {1104 <PalletNft<T>>::set_scoped_token_property(1105 resource_collection_id,1106 resource_id,1107 PropertyScope::Rmrk,1108 Self::rmrk_property(PendingResourceRemoval, &true)?,1109 )?;1110 }11111112 Ok(())1113 }11141115 fn change_collection_owner(1116 collection_id: CollectionId,1117 collection_type: misc::CollectionType,1118 sender: T::AccountId,1119 new_owner: T::AccountId,1120 ) -> DispatchResult {1121 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1122 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11231124 let mut collection = collection.into_inner();11251126 collection.owner = new_owner;1127 collection.save()1128 }11291130 fn check_collection_owner(1131 collection: &NonfungibleHandle<T>,1132 account: &T::CrossAccountId,1133 ) -> DispatchResult {1134 collection1135 .check_is_owner(account)1136 .map_err(Self::map_unique_err_to_proxy)1137 }11381139 pub fn last_collection_idx() -> RmrkCollectionId {1140 <CollectionIndex<T>>::get()1141 }11421143 pub fn unique_collection_id(1144 rmrk_collection_id: RmrkCollectionId,1145 ) -> Result<CollectionId, DispatchError> {1146 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1147 .map_err(|_| <Error<T>>::CollectionUnknown.into())1148 }11491150 pub fn rmrk_collection_id(1151 unique_collection_id: CollectionId,1152 ) -> Result<RmrkCollectionId, DispatchError> {1153 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1154 .map_err(|_| <Error<T>>::CollectionUnknown.into())1155 }11561157 pub fn get_nft_collection(1158 collection_id: CollectionId,1159 ) -> Result<NonfungibleHandle<T>, DispatchError> {1160 let collection = <CollectionHandle<T>>::try_get(collection_id)1161 .map_err(|_| <Error<T>>::CollectionUnknown)?;11621163 match collection.mode {1164 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1165 _ => Err(<Error<T>>::CollectionUnknown.into()),1166 }1167 }11681169 pub fn collection_exists(collection_id: CollectionId) -> bool {1170 <CollectionHandle<T>>::try_get(collection_id).is_ok()1171 }11721173 pub fn get_collection_property(1174 collection_id: CollectionId,1175 key: RmrkProperty,1176 ) -> Result<PropertyValue, DispatchError> {1177 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1178 .get(&Self::rmrk_property_key(key)?)1179 .ok_or(<Error<T>>::CollectionUnknown)?1180 .clone();11811182 Ok(collection_property)1183 }11841185 pub fn get_collection_property_decoded<V: Decode>(1186 collection_id: CollectionId,1187 key: RmrkProperty,1188 ) -> Result<V, DispatchError> {1189 Self::decode_property(Self::get_collection_property(collection_id, key)?)1190 }11911192 pub fn get_collection_type(1193 collection_id: CollectionId,1194 ) -> Result<misc::CollectionType, DispatchError> {1195 Self::get_collection_property_decoded(collection_id, CollectionType)1196 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1197 }11981199 pub fn ensure_collection_type(1200 collection_id: CollectionId,1201 collection_type: misc::CollectionType,1202 ) -> DispatchResult {1203 let actual_type = Self::get_collection_type(collection_id)?;1204 ensure!(1205 actual_type == collection_type,1206 <CommonError<T>>::NoPermission1207 );12081209 Ok(())1210 }12111212 pub fn get_typed_nft_collection(1213 collection_id: CollectionId,1214 collection_type: misc::CollectionType,1215 ) -> Result<NonfungibleHandle<T>, DispatchError> {1216 Self::ensure_collection_type(collection_id, collection_type)?;12171218 Self::get_nft_collection(collection_id)1219 }12201221 pub fn get_typed_nft_collection_mapped(1222 rmrk_collection_id: RmrkCollectionId,1223 collection_type: misc::CollectionType,1224 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1225 let unique_collection_id = match collection_type {1226 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1227 _ => rmrk_collection_id.into(),1228 };12291230 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12311232 Ok((collection, unique_collection_id))1233 }12341235 pub fn get_nft_property(1236 collection_id: CollectionId,1237 nft_id: TokenId,1238 key: RmrkProperty,1239 ) -> Result<PropertyValue, DispatchError> {1240 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1241 .get(&Self::rmrk_property_key(key)?)1242 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1243 .clone();12441245 Ok(nft_property)1246 }12471248 pub fn get_nft_property_decoded<V: Decode>(1249 collection_id: CollectionId,1250 nft_id: TokenId,1251 key: RmrkProperty,1252 ) -> Result<V, DispatchError> {1253 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1254 }12551256 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1257 <TokenData<T>>::contains_key((collection_id, nft_id))1258 }12591260 pub fn get_nft_type(1261 collection_id: CollectionId,1262 token_id: TokenId,1263 ) -> Result<NftType, DispatchError> {1264 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1265 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1266 }12671268 pub fn ensure_nft_type(1269 collection_id: CollectionId,1270 token_id: TokenId,1271 nft_type: NftType,1272 ) -> DispatchResult {1273 let actual_type = Self::get_nft_type(collection_id, token_id)?;1274 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);12751276 Ok(())1277 }12781279 pub fn ensure_nft_owner(1280 collection_id: CollectionId,1281 token_id: TokenId,1282 possible_owner: &T::CrossAccountId,1283 nesting_budget: &dyn budget::Budget,1284 ) -> DispatchResult {1285 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1286 possible_owner.clone(),1287 collection_id,1288 token_id,1289 None,1290 nesting_budget,1291 )1292 .map_err(Self::map_unique_err_to_proxy)?;12931294 ensure!(is_owned, <Error<T>>::NoPermission);12951296 Ok(())1297 }12981299 pub fn filter_user_properties<Key, Value, R, Mapper>(1300 collection_id: CollectionId,1301 token_id: Option<TokenId>,1302 filter_keys: Option<Vec<RmrkPropertyKey>>,1303 mapper: Mapper,1304 ) -> Result<Vec<R>, DispatchError>1305 where1306 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1307 Value: Decode + Default,1308 Mapper: Fn(Key, Value) -> R,1309 {1310 filter_keys1311 .map(|keys| {1312 let properties = keys1313 .into_iter()1314 .filter_map(|key| {1315 let key: Key = key.try_into().ok()?;13161317 let value = match token_id {1318 Some(token_id) => Self::get_nft_property_decoded(1319 collection_id,1320 token_id,1321 UserProperty(key.as_ref()),1322 ),1323 None => Self::get_collection_property_decoded(1324 collection_id,1325 UserProperty(key.as_ref()),1326 ),1327 }1328 .ok()?;13291330 Some(mapper(key, value))1331 })1332 .collect();13331334 Ok(properties)1335 })1336 .unwrap_or_else(|| {1337 let properties =1338 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13391340 Ok(properties)1341 })1342 }13431344 pub fn iterate_user_properties<Key, Value, R, Mapper>(1345 collection_id: CollectionId,1346 token_id: Option<TokenId>,1347 mapper: Mapper,1348 ) -> Result<impl Iterator<Item = R>, DispatchError>1349 where1350 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1351 Value: Decode + Default,1352 Mapper: Fn(Key, Value) -> R,1353 {1354 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;13551356 let properties = match token_id {1357 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1358 None => <PalletCommon<T>>::collection_properties(collection_id),1359 };13601361 let properties = properties.into_iter().filter_map(move |(key, value)| {1362 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;13631364 let key: Key = key.to_vec().try_into().ok()?;1365 let value: Value = value.decode().ok()?;13661367 Some(mapper(key, value))1368 });13691370 Ok(properties)1371 }13721373 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1374 map_unique_err_to_proxy! {1375 match err {1376 CommonError::NoPermission => NoPermission,1377 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1378 CommonError::PublicMintingNotAllowed => NoPermission,1379 CommonError::TokenNotFound => NoAvailableNftId,1380 CommonError::ApprovedValueTooLow => NoPermission,1381 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1382 StructureError::TokenNotFound => NoAvailableNftId,1383 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1384 }1385 }1386 }1387}pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -94,7 +94,8 @@
..Default::default()
};
- let collection_id_res = <PalletNft<T>>::init_collection(cross_sender.clone(), data);
+ let collection_id_res =
+ <PalletNft<T>>::init_collection(cross_sender.clone(), data, true);
if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {
return Err(<Error<T>>::NoAvailableBaseId.into());
@@ -155,6 +156,7 @@
misc::CollectionType::Base,
)
.map_err(|_| <Error<T>>::BaseDoesntExist)?;
+ collection.check_is_external()?;
if theme.name.as_slice() == b"default" {
<BaseHasDefaultTheme<T>>::insert(collection_id, true);
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -200,7 +200,7 @@
owner: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
) -> Result<CollectionId, DispatchError> {
- <PalletCommon<T>>::init_collection(owner, data)
+ <PalletCommon<T>>::init_collection(owner, data, false)
}
pub fn destroy_collection(
collection: RefungibleHandle<T>,
@@ -234,7 +234,6 @@
}
pub fn burn_token(collection: &RefungibleHandle<T>, token_id: TokenId) -> DispatchResult {
- collection.check_is_mutable()?;
let burnt = <TokensBurnt<T>>::get(collection.id)
.checked_add(1)
.ok_or(ArithmeticError::Overflow)?;
@@ -254,7 +253,6 @@
token: TokenId,
amount: u128,
) -> DispatchResult {
- collection.check_is_mutable()?;
let total_supply = <TotalSupply<T>>::get((collection.id, token))
.checked_sub(amount)
.ok_or(<CommonError<T>>::TokenValueTooLow)?;
@@ -327,7 +325,6 @@
amount: u128,
nesting_budget: &dyn Budget,
) -> DispatchResult {
- collection.check_is_mutable()?;
ensure!(
collection.limits.transfers_enabled(),
<CommonError<T>>::TransferNotAllowed
@@ -576,7 +573,6 @@
token: TokenId,
amount: u128,
) -> DispatchResult {
- collection.check_is_mutable()?;
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
collection.check_allowlist(spender)?;
pallets/unique/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/mod.rs
+++ b/pallets/unique/src/eth/mod.rs
@@ -92,8 +92,9 @@
..Default::default()
};
- let collection_id = <pallet_nonfungible::Pallet<T>>::init_collection(caller.clone(), data)
- .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
+ let collection_id =
+ <pallet_nonfungible::Pallet<T>>::init_collection(caller.clone(), data, false)
+ .map_err(pallet_evm_coder_substrate::dispatch_to_evm::<T>)?;
let address = pallet_common::eth::collection_id_to_address(collection_id);
Ok(address)
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -304,7 +304,7 @@
pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
- collection.check_is_mutable()?;
+ collection.check_is_internal()?;
// =========
@@ -339,6 +339,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_internal()?;
<PalletCommon<T>>::toggle_allowlist(
&collection,
@@ -373,6 +374,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_internal()?;
<PalletCommon<T>>::toggle_allowlist(
&collection,
@@ -407,7 +409,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
- target_collection.check_is_mutable()?;
+ target_collection.check_is_internal()?;
target_collection.check_is_owner(&sender)?;
target_collection.owner = new_owner.clone();
@@ -437,6 +439,7 @@
pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_internal()?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(
collection_id,
@@ -463,6 +466,7 @@
pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ collection.check_is_internal()?;
<Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(
collection_id,
@@ -488,6 +492,7 @@
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
target_collection.check_is_owner(&sender)?;
+ target_collection.check_is_internal()?;
target_collection.set_sponsor(new_sponsor.clone())?;
@@ -512,6 +517,7 @@
let sender = ensure_signed(origin)?;
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_internal()?;
ensure!(
target_collection.confirm_sponsorship(&sender)?,
Error::<T>::ConfirmUnsetSponsorFail
@@ -540,6 +546,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_internal()?;
target_collection.check_is_owner(&sender)?;
target_collection.sponsorship = SponsorshipState::Disabled;
@@ -704,6 +711,7 @@
pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_internal()?;
target_collection.check_is_owner(&sender)?;
// =========
@@ -858,6 +866,7 @@
) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_internal()?;
target_collection.check_is_owner(&sender)?;
let old_limit = &target_collection.limits;
@@ -879,6 +888,7 @@
) -> DispatchResult {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;
+ target_collection.check_is_internal()?;
target_collection.check_is_owner(&sender)?;
let old_limit = &target_collection.permissions;
runtime/common/src/dispatch.rsdiffbeforeafterboth--- a/runtime/common/src/dispatch.rs
+++ b/runtime/common/src/dispatch.rs
@@ -35,7 +35,7 @@
data: CreateCollectionData<T::AccountId>,
) -> DispatchResult {
let _id = match data.mode {
- CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, data)?,
+ CollectionMode::NFT => <PalletNonfungible<T>>::init_collection(sender, data, false)?,
CollectionMode::Fungible(decimal_points) => {
// check params
ensure!(