difftreelog
feat(rmrk-rpc) nft resources and priorities
in: master
6 files changed
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::*;24use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};25use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};26use pallet_evm::account::CrossAccountId;27use core::convert::AsRef;2829pub use pallet::*;3031pub mod misc;32pub mod property;3334use misc::*;35pub use property::*;3637use RmrkProperty::*;3839#[frame_support::pallet]40pub mod pallet {41 use super::*;42 use pallet_evm::account;4344 #[pallet::config]45 pub trait Config: frame_system::Config46 + pallet_common::Config47 + pallet_nonfungible::Config48 + account::Config {49 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;50 }5152 #[pallet::storage]53 #[pallet::getter(fn collection_index)]54 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5556 #[pallet::pallet]57 #[pallet::generate_store(pub(super) trait Store)]58 pub struct Pallet<T>(_);5960 #[pallet::event]61 #[pallet::generate_deposit(pub(super) fn deposit_event)]62 pub enum Event<T: Config> {63 CollectionCreated {64 issuer: T::AccountId,65 collection_id: RmrkCollectionId,66 },67 CollectionDestroyed {68 issuer: T::AccountId,69 collection_id: RmrkCollectionId,70 },71 IssuerChanged {72 old_issuer: T::AccountId,73 new_issuer: T::AccountId,74 collection_id: RmrkCollectionId,75 },76 CollectionLocked {77 issuer: T::AccountId,78 collection_id: RmrkCollectionId,79 },80 NftMinted {81 owner: T::AccountId,82 collection_id: RmrkCollectionId,83 nft_id: RmrkNftId,84 },85 NFTBurned {86 owner: T::AccountId,87 nft_id: RmrkNftId,88 },89 PropertySet {90 collection_id: RmrkCollectionId,91 maybe_nft_id: Option<RmrkNftId>,92 key: RmrkKeyString,93 value: RmrkValueString,94 },95 }9697 #[pallet::error]98 pub enum Error<T> {99 /* Unique-specific events */100 CorruptedCollectionType,101 NftTypeEncodeError,102 RmrkPropertyKeyIsTooLong,103 RmrkPropertyValueIsTooLong,104105 /* RMRK compatible events */106 CollectionNotEmpty,107 NoAvailableCollectionId,108 NoAvailableNftId,109 CollectionUnknown,110 NoPermission,111 CollectionFullOrLocked,112 }113114 #[pallet::call]115 impl<T: Config> Pallet<T> {116 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]117 #[transactional]118 pub fn create_collection(119 origin: OriginFor<T>,120 metadata: RmrkString,121 max: Option<u32>,122 symbol: RmrkCollectionSymbol,123 ) -> DispatchResult {124 let sender = ensure_signed(origin)?;125126 let limits = CollectionLimits {127 owner_can_transfer: Some(false),128 token_limit: max,129 ..Default::default()130 };131132 let data = CreateCollectionData {133 limits: Some(limits),134 token_prefix: symbol.into_inner()135 .try_into()136 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,137 ..Default::default()138 };139140 let collection_id_res = <PalletNft<T>>::init_collection(sender.clone(), data);141142 if let Err(DispatchError::Arithmetic(_)) = &collection_id_res {143 return Err(<Error<T>>::NoAvailableCollectionId.into());144 }145146 let collection_id = collection_id_res?;147148 <PalletCommon<T>>::set_scoped_collection_properties(149 collection_id,150 PropertyScope::Rmrk,151 [152 Self::rmrk_property(Metadata, &metadata)?,153 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,154 ].into_iter()155 )?;156157 <CollectionIndex<T>>::mutate(|n| *n += 1);158159 Self::deposit_event(Event::CollectionCreated {160 issuer: sender,161 collection_id: collection_id.0162 });163164 Ok(())165 }166167 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]168 #[transactional]169 pub fn destroy_collection(170 origin: OriginFor<T>,171 collection_id: RmrkCollectionId,172 ) -> DispatchResult {173 let sender = ensure_signed(origin)?;174 let cross_sender = T::CrossAccountId::from_sub(sender.clone());175176 let unique_collection_id = collection_id.into();177178 let collection = Self::get_typed_nft_collection(unique_collection_id, misc::CollectionType::Regular)?;179180 ensure!(collection.total_supply() == 0, <Error<T>>::CollectionNotEmpty);181182 <PalletNft<T>>::destroy_collection(collection, &cross_sender)183 .map_err(Self::map_common_err_to_proxy)?;184185 Self::deposit_event(Event::CollectionDestroyed { issuer: sender, collection_id });186187 Ok(())188 }189190 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]191 #[transactional]192 pub fn change_collection_issuer(193 origin: OriginFor<T>,194 collection_id: RmrkCollectionId,195 new_issuer: <T::Lookup as StaticLookup>::Source,196 ) -> DispatchResult {197 let sender = ensure_signed(origin)?;198199 let new_issuer = T::Lookup::lookup(new_issuer)?;200201 Self::change_collection_owner(202 collection_id.into(),203 misc::CollectionType::Regular,204 sender.clone(),205 new_issuer.clone()206 )?;207208 Self::deposit_event(Event::IssuerChanged {209 old_issuer: sender,210 new_issuer,211 collection_id,212 });213214 Ok(())215 }216217 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]218 #[transactional]219 pub fn lock_collection(220 origin: OriginFor<T>,221 collection_id: RmrkCollectionId,222 ) -> DispatchResult {223 let sender = ensure_signed(origin)?;224 let cross_sender = T::CrossAccountId::from_sub(sender.clone());225226 let collection = Self::get_typed_nft_collection(227 collection_id.into(),228 misc::CollectionType::Regular229 )?;230231 Self::check_collection_owner(&collection, &cross_sender)?;232233 let token_count = collection.total_supply();234235 let mut collection = collection.into_inner();236 collection.limits.token_limit = Some(token_count);237 collection.save()?;238239 Self::deposit_event(Event::CollectionLocked { issuer: sender, collection_id });240241 Ok(())242 }243244 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]245 #[transactional]246 pub fn mint_nft(247 origin: OriginFor<T>,248 owner: T::AccountId,249 collection_id: RmrkCollectionId,250 recipient: Option<T::AccountId>,251 royalty_amount: Option<Permill>,252 metadata: RmrkString,253 ) -> DispatchResult {254 let sender = ensure_signed(origin)?;255 let sender = T::CrossAccountId::from_sub(sender);256 let cross_owner = T::CrossAccountId::from_sub(owner.clone());257258 let royalty_info = royalty_amount.map(|amount| rmrk::RoyaltyInfo {259 recipient: recipient.unwrap_or_else(|| owner.clone()),260 amount261 });262263 let collection = Self::get_typed_nft_collection(264 collection_id.into(),265 misc::CollectionType::Regular,266 )?;267268 let nft_id = Self::create_nft(269 &sender,270 &cross_owner,271 &collection,272 NftType::Regular,273 [274 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,275 Self::rmrk_property(Metadata, &metadata)?,276 Self::rmrk_property(Equipped, &false)?,277 Self::rmrk_property(ResourceCollection, &None::<CollectionId>)?,278 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,279 ].into_iter()280 ).map_err(|err| match err {281 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),282 err => Self::map_common_err_to_proxy(err)283 })?;284285 Self::deposit_event(Event::NftMinted {286 owner,287 collection_id,288 nft_id: nft_id.0289 });290291 Ok(())292 }293294 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]295 #[transactional]296 pub fn burn_nft(297 origin: OriginFor<T>,298 collection_id: RmrkCollectionId,299 nft_id: RmrkNftId,300 ) -> DispatchResult {301 let sender = ensure_signed(origin)?;302 let cross_sender = T::CrossAccountId::from_sub(sender.clone());303304 Self::destroy_nft(305 cross_sender,306 collection_id.into(),307 misc::CollectionType::Regular,308 nft_id.into()309 )?;310311 Self::deposit_event(Event::NFTBurned { owner: sender, nft_id });312313 Ok(())314 }315316 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]317 #[transactional]318 pub fn set_property(319 origin: OriginFor<T>,320 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,321 maybe_nft_id: Option<RmrkNftId>,322 key: RmrkKeyString,323 value: RmrkValueString,324 ) -> DispatchResult {325 let sender = ensure_signed(origin)?;326 let sender = T::CrossAccountId::from_sub(sender);327328 let collection_id: CollectionId = rmrk_collection_id.into();329330 match maybe_nft_id {331 Some(nft_id) => {332 let token_id: TokenId = nft_id.into();333334 Self::ensure_nft_owner(collection_id, token_id, &sender)?;335 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;336337 <PalletNft<T>>::set_scoped_token_property(338 collection_id,339 token_id,340 PropertyScope::Rmrk,341 Self::rmrk_property(UserProperty(key.as_slice()), &value)?342 )?;343 },344 None => {345 let collection = Self::get_typed_nft_collection(346 collection_id,347 misc::CollectionType::Regular348 )?;349350 Self::check_collection_owner(&collection, &sender)?;351352 <PalletCommon<T>>::set_scoped_collection_property(353 collection_id,354 PropertyScope::Rmrk,355 Self::rmrk_property(UserProperty(key.as_slice()), &value)?356 )?;357 }358 }359360 Self::deposit_event(361 Event::PropertySet {362 collection_id: rmrk_collection_id,363 maybe_nft_id,364 key,365 value366 }367 );368369 Ok(())370 }371 }372}373374impl<T: Config> Pallet<T> {375 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {376 let key = rmrk_key.to_key::<T>()?;377378 let scoped_key = PropertyScope::Rmrk.apply(key)379 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;380381 Ok(scoped_key)382 }383384 pub fn rmrk_property<E: Encode>(rmrk_key: RmrkProperty, value: &E) -> Result<Property, DispatchError> {385 let key = rmrk_key.to_key::<T>()?;386387 let value = value.encode()388 .try_into()389 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;390391 let property = Property {392 key,393 value,394 };395396 Ok(property)397 }398399 pub fn create_nft(400 sender: &T::CrossAccountId,401 owner: &T::CrossAccountId,402 collection: &NonfungibleHandle<T>,403 nft_type: NftType,404 properties: impl Iterator<Item=Property>405 ) -> Result<TokenId, DispatchError> {406 let data = CreateNftExData {407 const_data: nft_type.encode()408 .try_into()409 .map_err(|_| <Error<T>>::NftTypeEncodeError)?,410 properties: BoundedVec::default(),411 owner: owner.clone(),412 };413414 let budget = budget::Value::new(2);415416 <PalletNft<T>>::create_item(417 collection,418 sender,419 data,420 &budget,421 )?;422423 let nft_id = <PalletNft<T>>::current_token_id(collection.id);424425 <PalletNft<T>>::set_scoped_token_properties(426 collection.id,427 nft_id,428 PropertyScope::Rmrk,429 properties430 )?;431432 Ok(nft_id)433 }434435 fn destroy_nft(436 sender: T::CrossAccountId,437 collection_id: CollectionId,438 collection_type: misc::CollectionType,439 token_id: TokenId440 ) -> DispatchResult {441 let collection = Self::get_typed_nft_collection(442 collection_id,443 collection_type444 )?;445446 <PalletNft<T>>::burn(&collection, &sender, token_id)447 .map_err(Self::map_common_err_to_proxy)?;448449 Ok(())450 }451452 fn change_collection_owner(453 collection_id: CollectionId,454 collection_type: misc::CollectionType,455 sender: T::AccountId,456 new_owner: T::AccountId,457 ) -> DispatchResult {458 let collection = Self::get_typed_nft_collection(459 collection_id,460 collection_type461 )?;462 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;463464 let mut collection = collection.into_inner();465466 collection.owner = new_owner;467 collection.save()468 }469470 fn check_collection_owner(collection: &NonfungibleHandle<T>, account: &T::CrossAccountId) -> DispatchResult {471 collection.check_is_owner(account)472 .map_err(Self::map_common_err_to_proxy)473 }474475 pub fn last_collection_idx() -> RmrkCollectionId {476 <CollectionIndex<T>>::get()477 }478479 pub fn get_nft_collection(collection_id: CollectionId) -> Result<NonfungibleHandle<T>, DispatchError> {480 let collection = <CollectionHandle<T>>::try_get(collection_id)481 .map_err(|_| <Error<T>>::CollectionUnknown)?;482483 match collection.mode {484 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),485 _ => Err(<Error<T>>::CollectionUnknown.into())486 }487 }488489 // should this even be here, might displace it to common/nonfungible -- but they did not need it, only rmrk does490 pub fn collection_exists(collection_id: CollectionId) -> bool {491 <pallet_common::CollectionById<T>>::contains_key(collection_id)492 }493494 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {495 <TokenData<T>>::contains_key((collection_id, nft_id))496 }497498 pub fn get_collection_property(collection_id: CollectionId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {499 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)500 .get(&Self::rmrk_property_key(key)?)501 .ok_or(<Error<T>>::CollectionUnknown)?502 .clone();503504 Ok(collection_property)505 }506507 pub fn get_collection_type(collection_id: CollectionId) -> Result<misc::CollectionType, DispatchError> {508 let value = Self::get_collection_property(collection_id, CollectionType)?;509510 let mut value = value.as_slice();511512 misc::CollectionType::decode(&mut value)513 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())514 }515516 pub fn ensure_collection_type(collection_id: CollectionId, collection_type: misc::CollectionType) -> DispatchResult {517 let actual_type = Self::get_collection_type(collection_id)?;518 ensure!(actual_type == collection_type, <CommonError<T>>::NoPermission);519520 Ok(())521 }522523 pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {524 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))525 .get(&Self::rmrk_property_key(key)?)526 .ok_or(<Error<T>>::NoAvailableNftId)?527 .clone();528529 Ok(nft_property)530 }531532 pub fn get_nft_type(collection_id: CollectionId, token_id: TokenId) -> Result<NftType, DispatchError> {533 let token_data = <TokenData<T>>::get((collection_id, token_id))534 .ok_or(<Error<T>>::NoAvailableNftId)?;535536 let mut const_data = token_data.const_data.as_slice();537538 NftType::decode(&mut const_data).map_err(|_| <Error<T>>::NoAvailableNftId.into())539 }540541 pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult {542 let actual_type = Self::get_nft_type(collection_id, token_id)?;543 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);544545 Ok(())546 }547548 pub fn ensure_nft_owner(549 collection_id: CollectionId,550 token_id: TokenId,551 possible_owner: &T::CrossAccountId552 ) -> DispatchResult {553 let token_data = <TokenData<T>>::get((collection_id, token_id))554 .ok_or(<Error<T>>::NoAvailableNftId)?;555556 ensure!(token_data.owner == *possible_owner, <Error<T>>::NoPermission);557558 Ok(())559 }560561 pub fn filter_user_properties<Key, Value, R, Mapper>(562 collection_id: CollectionId,563 token_id: Option<TokenId>,564 filter_keys: Option<Vec<RmrkPropertyKey>>,565 mapper: Mapper,566 ) -> Result<Vec<R>, DispatchError>567 where568 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,569 Value: Decode + Default,570 Mapper: Fn(Key, Value) -> R571 {572 filter_keys.map(|keys| {573 let properties = keys.into_iter()574 .filter_map(|key| {575 let key: Key = key.try_into().ok()?;576577 let value = match token_id {578 Some(token_id) => Self::get_nft_property(579 collection_id,580 token_id,581 UserProperty(key.as_ref())582 ),583 None => Self::get_collection_property(584 collection_id,585 UserProperty(key.as_ref())586 )587 }.ok()?.decode_or_default();588589 Some(mapper(key, value))590 })591 .collect();592593 Ok(properties)594 }).unwrap_or_else(|| {595 let properties = Self::iterate_user_properties(collection_id, token_id, mapper)?596 .collect();597598 Ok(properties)599 })600 }601602 pub fn iterate_user_properties<Key, Value, R, Mapper>(603 collection_id: CollectionId,604 token_id: Option<TokenId>,605 mapper: Mapper,606 ) -> Result<impl Iterator<Item=R>, DispatchError>607 where608 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,609 Value: Decode + Default,610 Mapper: Fn(Key, Value) -> R611 {612 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;613614 let properties = match token_id {615 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),616 None => <PalletCommon<T>>::collection_properties(collection_id)617 };618619 let properties = properties620 .into_iter()621 .filter_map(move |(key, value)| {622 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;623624 let key: Key = key.to_vec().try_into().ok()?;625 let value: Value = value.decode_or_default();626627 Some(mapper(key, value))628 });629630 Ok(properties)631 }632633 pub fn get_typed_nft_collection(634 collection_id: CollectionId,635 collection_type: misc::CollectionType636 ) -> Result<NonfungibleHandle<T>, DispatchError> {637 Self::ensure_collection_type(collection_id, collection_type)?;638639 Self::get_nft_collection(collection_id)640 }641642 fn map_common_err_to_proxy(err: DispatchError) -> DispatchError {643 map_common_err_to_proxy! {644 match err {645 NoPermission => NoPermission,646 CollectionTokenLimitExceeded => CollectionFullOrLocked,647 PublicMintingNotAllowed => NoPermission,648 TokenNotFound => NoAvailableNftId649 }650 }651 }652}pallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/misc.rs
+++ b/pallets/proxy-rmrk-core/src/misc.rs
@@ -26,18 +26,6 @@
}
}
-pub trait RmrkRebind<T, S> {
- fn rebind(&self) -> BoundedVec<u8, S>;
-}
-
-impl<T, S> RmrkRebind<T, S> for BoundedVec<u8, T> where BoundedVec<u8, S>: TryFrom<Vec<u8>> {
- fn rebind(&self) -> BoundedVec<u8, S> {
- BoundedVec::<u8, S>::try_from(
- self.clone().into_inner()
- ).unwrap_or_default()
- }
-}
-
#[derive(Encode, Decode, PartialEq, Eq)]
pub enum CollectionType {
Regular,
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -49,6 +49,7 @@
},
NftChild as RmrkNftChild, AccountIdOrCollectionNftTuple as RmrkAccountIdOrCollectionNftTuple,
FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart, EquippableList as RmrkEquippableList,
+ BasicResource as RmrkBasicResource, ComposableResource as RmrkComposableResource, SlotResource as RmrkSlotResource,
};
mod bounded;
@@ -925,17 +926,14 @@
}
}
-pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;
pub type RmrkCollectionInfo<AccountId> =
CollectionInfo<RmrkString, RmrkCollectionSymbol, AccountId>;
pub type RmrkInstanceInfo<AccountId> = NftInfo<AccountId, Permill, RmrkString>;
pub type RmrkResourceInfo = ResourceInfo<
- BoundedVec<u8, RmrkResourceSymbolLimit>,
+ RmrkBoundedResource,
RmrkString,
- BoundedVec<RmrkPartId, RmrkPartsLimit>,
+ RmrkBoundedParts,
>;
-pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;
-pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;
pub type RmrkPropertyInfo = PropertyInfo<RmrkKeyString, RmrkValueString>;
pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;
pub type RmrkPartType =
@@ -943,6 +941,13 @@
pub type RmrkThemeProperty = ThemeProperty<RmrkString>;
pub type RmrkTheme = Theme<RmrkString, Vec<RmrkThemeProperty>>;
+pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;
+pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;
+pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;
+
+type RmrkBoundedResource = BoundedVec<u8, RmrkResourceSymbolLimit>;
+type RmrkBoundedParts = BoundedVec<RmrkPartId, RmrkPartsLimit>;
+
pub type RmrkRpcString = Vec<u8>;
pub type RmrkThemeName = RmrkRpcString;
pub type RmrkPropertyKey = RmrkRpcString;
primitives/data-structs/src/rmrk.rsdiffbeforeafterboth--- a/primitives/data-structs/src/rmrk.rs
+++ b/primitives/data-structs/src/rmrk.rs
@@ -154,7 +154,7 @@
pub value: BoundedValue,
}
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]
+#[derive(Encode, Decode, Default, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(Serialize))]
#[cfg_attr(
feature = "std",
@@ -275,7 +275,7 @@
pub thumb: Option<BoundedString>,
}
-#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]
+#[derive(Encode, Decode, Derivative, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "std", derive(Serialize))]
#[cfg_attr(
feature = "std",
@@ -286,7 +286,9 @@
"#
)
)]
-pub enum ResourceTypes<BoundedString, BoundedParts> {
+#[derivative(Default(bound=""))]
+pub enum ResourceTypes<BoundedString: Default, BoundedParts> {
+ #[derivative(Default)]
Basic(BasicResource<BoundedString>),
Composable(ComposableResource<BoundedString, BoundedParts>),
Slot(SlotResource<BoundedString>),
@@ -305,7 +307,7 @@
"#
)
)]
-pub struct ResourceInfo<BoundedResource, BoundedString, BoundedParts> {
+pub struct ResourceInfo<BoundedResource, BoundedString: Default, BoundedParts> {
/// id is a 5-character string of reasonable uniqueness.
/// The combination of base ID and resource id should be unique across the entire RMRK
/// ecosystem which
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -146,9 +146,7 @@
}
fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {
- use frame_support::BoundedVec;
- use scale_info::prelude::string::String;
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode}};
+ use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkDecode}};
let collection_id = CollectionId(collection_id);
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
@@ -156,20 +154,18 @@
Err(_) => return Ok(None),
};
- let nfts_count = (dispatch_unique_runtime!(collection_id.total_supply()) as Result<u32, DispatchError>)?;
- //<Runtime as up_rpc::UniqueApi>::total_supply(collection_id); // todo can't find UniqueApi with disabled default features
+ let nfts_count = dispatch_unique_runtime!(collection_id.total_supply())?;
Ok(Some(RmrkCollectionInfo {
issuer: collection.owner.clone(),
metadata: RmrkCore::get_collection_property(collection_id, RmrkProperty::Metadata)?.decode_or_default(),
max: collection.limits.token_limit,
- symbol: collection.token_prefix.rebind(), // change
+ symbol: collection.token_prefix.decode_or_default(),
nfts_count
}))
}
fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {
- use frame_support::BoundedVec;
use up_data_structs::mapping::TokenAddressMapping;
use pallet_proxy_rmrk_core::{RmrkProperty, misc::RmrkDecode};
@@ -177,7 +173,7 @@
let nft_id = TokenId(nft_by_id);
if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }
- let owner = match (dispatch_unique_runtime!(collection_id.token_owner(nft_id)) as Result<Option<CrossAccountId>, DispatchError>)? {
+ let owner = match dispatch_unique_runtime!(collection_id.token_owner(nft_id))? {
Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),
None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
@@ -197,16 +193,17 @@
}
fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {
+ use pallet_proxy_rmrk_core::misc::CollectionType;
+
let cross_account_id = CrossAccountId::from_sub(account_id);
let collection_id = CollectionId(collection_id);
- if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); }
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
Ok(
- (dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id)) as Result<Vec<TokenId>, DispatchError>)?
- //<Runtime as up_rpc::UniqueApi<Block, CrossAccountId, AccountId>>::account_tokens(collection_id, cross_account_id)?
+ dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id))?
.into_iter()
.map(|token| token.0)
- .collect::<Vec<_>>()
+ .collect()
)
}
@@ -226,8 +223,7 @@
.map(|(child_id, _)| RmrkNftChild {
collection_id: collection_id.0, // todo make sure they're always from this collection // spoiler: they're not
nft_id: child_id.0,
- })
- .collect()
+ }).collect()
)
}
@@ -277,28 +273,70 @@
fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
use frame_support::BoundedVec;
- use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
+ use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};
let collection_id = CollectionId(collection_id);
- if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }
+ if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); } // todo make sure the collection type doesn't matter
let nft_id = TokenId(nft_id);
if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }
- Ok(Vec::new(/*[RmrkResourceInfo {
+ let resource_collection_id: CollectionId = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourceCollection)
+ .unwrap()
+ .decode_or_default();
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }
- }]*/))
+ let resources = pallet_nonfungible::TokenProperties::<Runtime>::iter_prefix((resource_collection_id,))
+ .filter_map(|(resource_id, properties)| Some(RmrkResourceInfo {
+ id: BoundedVec::default(), // todo ResourceId property
+ pending: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::PendingResourceAccept).unwrap().decode_or_default(),
+ pending_removal: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::PendingResourceRemoval).unwrap().decode_or_default(),
+ resource: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::ResourceType).unwrap().decode_or_default(),/* {
+ RmrkResourceTypes::Basic(resource) => RmrkResourceTypes::Basic(),/*(RmrkBasicResource {
+ src: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Src).unwrap().decode_or_default(),
+ metadata: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Metadata).unwrap().decode_or_default(),
+ license: RmrkCore::get_nft_property_inner(properties, RmrkProperty::License).unwrap().decode_or_default(),
+ thumb: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Thumb).unwrap().decode_or_default(),
+ },*///BasicResource<BoundedString>)
+ _ => todo!(), //RmrkResourceTypes::Composable(ComposableResource<BoundedString, BoundedParts>),
+ //RmrkResourceTypes::Slot(SlotResource<BoundedString>),
+ },*/
+ }))
+ .collect();
+
+ Ok(resources)
}
fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {
- todo!()
+ use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};
+
+ let collection_id = CollectionId(collection_id);
+ if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); } // todo ensure the collection type doesn't matter
+
+ let nft_id = TokenId(nft_id);
+ if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }
+
+ /*let resource_collection_id: CollectionId = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourceCollection)
+ .unwrap()
+ .decode_or_default();
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }
+
+ let resources = pallet_nonfungible::TokenProperties::<Runtime>::iter_prefix((resource_collection_id,))
+ .filter_map(|(resource_id, properties)| Some((
+ resource_id, // ResourceId property
+ RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::Priority).unwrap().decode_or_default(),
+ )))
+ .collect()
+ .sort_by_key(|(_, index)| *index)
+ .into_iter().map(|(resource_id, _)| resource_id)*/
+ let priorities = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourcePriorities)?.decode_or_default();
+
+ Ok(priorities)
}
fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {
- use frame_support::BoundedVec;
- use scale_info::prelude::string::String;
use pallet_proxy_rmrk_core::{
- RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode},
+ RmrkProperty, misc::{CollectionType, RmrkDecode},
};
let collection_id = CollectionId(base_id);
@@ -310,18 +348,17 @@
Ok(Some(RmrkBaseInfo {
issuer: collection.owner.clone(),
base_type: RmrkCore::get_collection_property(collection_id, RmrkProperty::BaseType)?.decode_or_default(),
- symbol: collection.token_prefix.rebind(),
+ symbol: collection.token_prefix.decode_or_default(),
}))
}
fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
- use frame_support::BoundedVec;
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};
let collection_id = CollectionId(base_id);
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
- let parts = (dispatch_unique_runtime!(collection_id.collection_tokens()))?
+ let parts = dispatch_unique_runtime!(collection_id.collection_tokens())?
.into_iter()
.filter_map(|token_id| {
let nft_type = RmrkCore::get_nft_type(collection_id, token_id).ok()?;
@@ -347,7 +384,6 @@
}
fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
- use frame_support::BoundedVec;
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkDecode}};
let collection_id = CollectionId(base_id);
@@ -355,7 +391,7 @@
return Ok(Vec::new());
}
- let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()))?
+ let theme_names = dispatch_unique_runtime!(collection_id.collection_tokens())?
.iter()
.filter_map(|token_id| {
let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
@@ -373,7 +409,6 @@
}
fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {
- use frame_support::BoundedVec;
use pallet_proxy_rmrk_core::{
RmrkProperty,
misc::{CollectionType, NftType, RmrkDecode}
@@ -384,7 +419,7 @@
return Ok(None);
}
- let theme_info = (dispatch_unique_runtime!(collection_id.collection_tokens()))?
+ let theme_info = dispatch_unique_runtime!(collection_id.collection_tokens())?
.into_iter()
.find_map(|token_id| {
RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;
tests/src/nesting/properties.test.tsdiffbeforeafterboth--- a/tests/src/nesting/properties.test.ts
+++ b/tests/src/nesting/properties.test.ts
@@ -711,7 +711,7 @@
});
});
- it('Forbids changing/deleting properties of a token if the property is permanent (constant)', async () => {
+ it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {
await usingApi(async api => {
let i = -1;
for (const permission of constitution) {