git.delta.rocks / unique-network / refs/commits / 9e65253a1d22

difftreelog

feat(rmrk-rpc) rpc refactoring

Fahrrader2022-05-25parent: #d364b89.patch.diff
in: master

6 files changed

modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -22,7 +22,7 @@
 use sp_std::vec::Vec;
 use up_data_structs::*;
 use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};
-use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};
+use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};
 use pallet_evm::account::CrossAccountId;
 
 pub use pallet::*;
@@ -396,6 +396,15 @@
         Ok(collection)
     }
 
+    // should this even be here, might displace it to common/nonfungible -- but they did not need it, only rmrk does
+    pub fn collection_exists(collection_id: CollectionId) -> bool {
+        <pallet_common::CollectionById<T>>::contains_key(collection_id)
+    }
+
+    pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {
+        <TokenData<T>>::contains_key((collection_id, nft_id))
+    }
+
     pub fn get_collection_property(collection_id: CollectionId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
         let collection_property = <PalletCommon<T>>::collection_properties(collection_id)
             .get(&rmrk_property!(Config=T, key)?)
@@ -414,6 +423,13 @@
         Ok(collection_type)
     }
 
+    pub fn ensure_collection_type(collection_id: CollectionId, collection_type: CollectionType) -> DispatchResult {
+        let actual_type = Self::get_collection_type(collection_id)?;
+        ensure!(actual_type == collection_type, <CommonError<T>>::NoPermission);
+
+        Ok(())
+    }
+
     pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
         let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
             .get(&rmrk_property!(Config=T, key)?)
@@ -423,9 +439,16 @@
         Ok(nft_property)
     }
 
-    pub fn check_collection_type(collection_id: CollectionId, collection_type: CollectionType) -> DispatchResult {
-        let actual_type = Self::get_collection_type(collection_id)?;
-        ensure!(actual_type == collection_type, <CommonError<T>>::NoPermission);
+    pub fn get_nft_type(collection_id: CollectionId, token_id: TokenId) -> Result<NftType, DispatchError> {
+        <TokenData<T>>::get((collection_id, token_id))
+            .unwrap()
+            .rmrk_nft_type()
+            .ok_or(<Error<T>>::NoAvailableNftId.into())
+    }
+
+    pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult {
+        let actual_type = Self::get_nft_type(collection_id, token_id)?;
+        ensure!(actual_type == nft_type, <CommonError<T>>::NoPermission);
 
         Ok(())
     }
@@ -434,7 +457,7 @@
         collection_id: CollectionId,
         collection_type: CollectionType
     ) -> Result<NonfungibleHandle<T>, DispatchError> {
-        Self::check_collection_type(collection_id, collection_type)?;
+        Self::ensure_collection_type(collection_id, collection_type)?;
 
         Self::get_nft_collection(collection_id)
     }
modifiedpallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/misc.rs
+++ b/pallets/proxy-rmrk-core/src/misc.rs
@@ -82,15 +82,27 @@
     }
 }
 
-pub trait RmrkDecode<T: Decode> {
-    fn decode_property(&self) -> Option<T>;
+pub trait RmrkDecode<T: Decode + Default, S> {
+    fn decode_or_default(&self) -> T;
 }
 
-impl<T: Decode> RmrkDecode<T> for RmrkString {
-    fn decode_property(&self) -> Option<T> { // todo access runtime errors? // but then rmrk_nft_type must have it too
+impl<T: Decode + Default, S> RmrkDecode<T, S> for BoundedVec<u8, S> {
+    fn decode_or_default(&self) -> T {
         let mut value = self.as_slice();
 
-        T::decode(&mut value).ok()
+        T::decode(&mut value).unwrap_or_default()
+    }
+}
+
+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()
     }
 }
 
modifiedprimitives/data-structs/src/rmrk.rsdiffbeforeafterboth
before · primitives/data-structs/src/rmrk.rs
1use codec::{Decode, Encode, MaxEncodedLen};2use scale_info::TypeInfo;345#[cfg(feature = "std")]6use serde::Serialize;78use primitives::*;910pub mod primitives {11	pub type CollectionId = u32;12	pub type ResourceId = u32;13	pub type NftId = u32;14	pub type BaseId = u32;15	pub type SlotId = u32;16	pub type PartId = u32;17	pub type ZIndex = u32;18}1920#[cfg(feature = "std")]21mod serialize {22    use core::convert::AsRef;23    use serde::ser::{self, Serialize};2425    pub mod vec {26        use super::*;2728        pub fn serialize<D, V, C>(value: &C, serializer: D) -> Result<D::Ok, D::Error>29        where30            D: ser::Serializer,31            V: Serialize,32            C: AsRef<[V]>,33        {34            value.as_ref().serialize(serializer)35        }36    }3738    pub mod opt_vec {39        use super::*;4041        pub fn serialize<D, V, C>(value: &Option<C>, serializer: D) -> Result<D::Ok, D::Error>42        where43            D: ser::Serializer,44            V: Serialize,45            C: AsRef<[V]>,46        {47            match value {48                Some(value) => super::vec::serialize(value, serializer),49                None => serializer.serialize_none()50            }51        }52    }53}5455/// Collection info.56#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]57#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]58#[cfg_attr(59	feature = "std",60	serde(61		bound = r#"62			AccountId: Serialize,63			BoundedString: AsRef<[u8]>,64			BoundedSymbol: AsRef<[u8]>65		"#66	)67)]68pub struct CollectionInfo<BoundedString, BoundedSymbol, AccountId> {69	/// Current bidder and bid price.70	pub issuer: AccountId,7172	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]73	pub metadata: BoundedString,74	pub max: Option<u32>,7576	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]77	pub symbol: BoundedSymbol,78	pub nfts_count: u32,79}8081#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, Debug, TypeInfo, MaxEncodedLen)]82#[cfg_attr(feature = "std", derive(Serialize))]83pub enum AccountIdOrCollectionNftTuple<AccountId> {84	AccountId(AccountId),85	CollectionAndNftTuple(CollectionId, NftId),86}8788/// Royalty information (recipient and amount)89#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]90#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]91pub struct RoyaltyInfo<AccountId, RoyaltyAmount> {92	/// Recipient (AccountId) of the royalty93    pub recipient: AccountId,94	/// Amount (Permill) of the royalty95    pub amount: RoyaltyAmount,96}9798/// Nft info.99#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]100#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]101#[cfg_attr(102	feature = "std",103	serde(104		bound = r#"105			AccountId: Serialize,106			RoyaltyAmount: Serialize,107			BoundedString: AsRef<[u8]>108		"#109	)110)]111pub struct NftInfo<AccountId, RoyaltyAmount, BoundedString> {112	/// The owner of the NFT, can be either an Account or a tuple (CollectionId, NftId)113	pub owner: AccountIdOrCollectionNftTuple<AccountId>,114	/// Royalty (optional)115	pub royalty: Option<RoyaltyInfo<AccountId, RoyaltyAmount>>,116117	/// Arbitrary data about an instance, e.g. IPFS hash118	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]119	pub metadata: BoundedString,120121	/// Equipped state122	pub equipped: bool,123	/// Pending state (if sent to NFT)124	pub pending: bool,125}126127#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]128#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]129pub struct NftChild {130	pub collection_id: CollectionId,131	pub nft_id: NftId132}133134#[cfg_attr(feature = "std", derive(Serialize))]135#[derive(Encode, Decode, PartialEq, TypeInfo)]136#[cfg_attr(137	feature = "std",138	serde(139		bound = r#"140			BoundedKey: AsRef<[u8]>,141			BoundedValue: AsRef<[u8]>142		"#143	)144)]145pub struct PropertyInfo<BoundedKey, BoundedValue>146{147	/// Key of the property148	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]149	pub key: BoundedKey,150151	/// Value of the property152	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]153	pub value: BoundedValue,154}155156#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]157#[cfg_attr(feature = "std", derive(Serialize))]158#[cfg_attr(159	feature = "std",160	serde(bound = "BoundedString: AsRef<[u8]>")161)]162pub struct BasicResource<BoundedString> {163	/// If the resource is Media, the base property is absent. Media src should be a URI like an164	/// IPFS hash.165	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]166	pub src: Option<BoundedString>,167168	/// Reference to IPFS location of metadata169	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]170	pub metadata: Option<BoundedString>,171172	/// Optional location or identier of license173	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]174	pub license: Option<BoundedString>,175176	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given177	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is178	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns179	/// another bird, showing the full render of one bird inside the other's inventory might be a180	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an181	/// image that is lighter and faster to load but representative of this resource.182	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]183	pub thumb: Option<BoundedString>,184}185186#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "std", derive(Serialize))]188#[cfg_attr(189	feature = "std",190	serde(191		bound = r#"192			BoundedString: AsRef<[u8]>,193			BoundedParts: AsRef<[PartId]>194		"#195	)196)]197pub struct ComposableResource<BoundedString, BoundedParts> {198	/// If a resource is composed, it will have an array of parts that compose it199	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]200	pub parts: BoundedParts,201202	/// A Base is uniquely identified by the combination of the word `base`, its minting block203	/// number, and user provided symbol during Base creation, glued by dashes `-`, e.g.204	/// base-4477293-kanaria_superbird.205	pub base: BaseId,206207	/// If the resource is Media, the base property is absent. Media src should be a URI like an208	/// IPFS hash.209	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]210	pub src: Option<BoundedString>,211212	/// Reference to IPFS location of metadata213	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]214	pub metadata: Option<BoundedString>,215216	/// If the resource has the slot property, it was designed to fit into a specific Base's slot.217	/// The baseslot will be composed of two dot-delimited values, like so:218	/// "base-4477293-kanaria_superbird.machine_gun_scope". This means: "This resource is219	/// compatible with the machine_gun_scope slot of base base-4477293-kanaria_superbird220221	/// Optional location or identier of license222	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]223	pub license: Option<BoundedString>,224225	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given226	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is227	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns228	/// another bird, showing the full render of one bird inside the other's inventory might be a229	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an230	/// image that is lighter and faster to load but representative of this resource.231	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]232	pub thumb: Option<BoundedString>,233}234235#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]236#[cfg_attr(feature = "std", derive(Serialize))]237#[cfg_attr(238	feature = "std",239	serde(bound = "BoundedString: AsRef<[u8]>")240)]241pub struct SlotResource<BoundedString> {242	/// A Base is uniquely identified by the combination of the word `base`, its minting block243	/// number, and user provided symbol during Base creation, glued by dashes `-`, e.g.244	/// base-4477293-kanaria_superbird.245	pub base: BaseId,246247	/// If the resource is Media, the base property is absent. Media src should be a URI like an248	/// IPFS hash.249	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]250	pub src: Option<BoundedString>,251252	/// Reference to IPFS location of metadata253	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]254	pub metadata: Option<BoundedString>,255256	/// If the resource has the slot property, it was designed to fit into a specific Base's slot.257	/// The baseslot will be composed of two dot-delimited values, like so:258	/// "base-4477293-kanaria_superbird.machine_gun_scope". This means: "This resource is259	/// compatible with the machine_gun_scope slot of base base-4477293-kanaria_superbird260	pub slot: SlotId,261262	/// The license field, if present, should contain a link to a license (IPFS or static HTTP263	/// url), or an identifier, like RMRK_nocopy or ipfs://ipfs/someHashOfLicense.264	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]265	pub license: Option<BoundedString>,266267	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given268	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is269	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns270	/// another bird, showing the full render of one bird inside the other's inventory might be a271	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an272	/// image that is lighter and faster to load but representative of this resource.273	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]274	pub thumb: Option<BoundedString>,275}276277#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]278#[cfg_attr(feature = "std", derive(Serialize))]279#[cfg_attr(280	feature = "std",281	serde(282		bound = r#"283			BoundedString: AsRef<[u8]>,284			BoundedParts: AsRef<[PartId]>285		"#286	)287)]288pub enum ResourceTypes<BoundedString, BoundedParts> {289	Basic(BasicResource<BoundedString>),290	Composable(ComposableResource<BoundedString, BoundedParts>),291	Slot(SlotResource<BoundedString>),292}293294295#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]296#[cfg_attr(feature = "std", derive(Serialize))]297#[cfg_attr(298	feature = "std",299	serde(300		bound = r#"301			BoundedResource: AsRef<[u8]>,302			BoundedString: AsRef<[u8]>,303			BoundedParts: AsRef<[PartId]>304		"#305	)306)]307pub struct ResourceInfo<BoundedResource, BoundedString, BoundedParts> {308	/// id is a 5-character string of reasonable uniqueness.309	/// The combination of base ID and resource id should be unique across the entire RMRK310	/// ecosystem which311	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]312	pub id: BoundedResource,313314	/// Resource315	pub resource: ResourceTypes<BoundedString, BoundedParts>,316317	/// If resource is sent to non-rootowned NFT, pending will be false and need to be accepted318	pub pending: bool,319320	/// If resource removal request is sent by non-rootowned NFT, pending will be true and need to be accepted321	pub pending_removal: bool,322}323324#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]325#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]326#[cfg_attr(327	feature = "std",328	serde(329		bound = r#"330			AccountId: Serialize,331			BoundedString: AsRef<[u8]>332		"#333	)334)]335pub struct BaseInfo<AccountId, BoundedString> {336	/// Original creator of the Base337	pub issuer: AccountId,338339	/// Specifies how an NFT should be rendered, ie "svg"340	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]341	pub base_type: BoundedString,342343	/// User provided symbol during Base creation344	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]345	pub symbol: BoundedString,346}347348#[cfg_attr(feature = "std", derive(Serialize))]349#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]350#[cfg_attr(351	feature = "std",352	serde(bound = "BoundedString: AsRef<[u8]>")353)]354pub struct FixedPart<BoundedString> {355	pub id: PartId,356	pub z: ZIndex,357358	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]359	pub src: BoundedString,360}361362#[cfg_attr(feature = "std", derive(Serialize))]363#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]364#[cfg_attr(365	feature = "std",366	serde(bound = "BoundedCollectionList: AsRef<[CollectionId]>")367)]368pub enum EquippableList<BoundedCollectionList> {369	All,370	Empty,371	Custom(372		#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]373		BoundedCollectionList374	),375}376377#[cfg_attr(feature = "std", derive(Serialize))]378#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]379#[cfg_attr(380	feature = "std",381	serde(382		bound = r#"383			BoundedString: AsRef<[u8]>,384			BoundedCollectionList: AsRef<[CollectionId]>385		"#386	)387)]388pub struct SlotPart<BoundedString, BoundedCollectionList> {389	pub id: PartId,390	pub equippable: EquippableList<BoundedCollectionList>,391392	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]393	pub src: BoundedString,394395	pub z: ZIndex,396}397398#[cfg_attr(feature = "std", derive(Serialize))]399#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]400#[cfg_attr(401	feature = "std",402	serde(403		bound = r#"404			BoundedString: AsRef<[u8]>,405			BoundedCollectionList: AsRef<[CollectionId]>406		"#407	)408)]409pub enum PartType<BoundedString, BoundedCollectionList> {410	FixedPart(FixedPart<BoundedString>),411	SlotPart(SlotPart<BoundedString, BoundedCollectionList>),412}413414#[cfg_attr(feature = "std", derive(Eq, Serialize))]415#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq)]416#[cfg_attr(417	feature = "std",418	serde(419		bound = r#"420			BoundedString: AsRef<[u8]>,421			PropertyList: AsRef<[ThemeProperty<BoundedString>]>,422		"#423	)424)]425pub struct Theme<BoundedString, PropertyList> {426	/// Name of the theme427	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]428	pub name: BoundedString,429430	/// Theme properties431	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]432	pub properties: PropertyList,433	/// Inheritability434	pub inherit: bool,435}436437#[cfg_attr(feature = "std", derive(Eq, Serialize))]438#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq)]439#[cfg_attr(440	feature = "std",441	serde(bound = "BoundedString: AsRef<[u8]>")442)]443pub struct ThemeProperty<BoundedString> {444	/// Key of the property445	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]446	pub key: BoundedString,447448	/// Value of the property449	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]450	pub value: BoundedString,451}
after · primitives/data-structs/src/rmrk.rs
1use codec::{Decode, Encode, MaxEncodedLen};2use scale_info::TypeInfo;345#[cfg(feature = "std")]6use serde::Serialize;78use primitives::*;910pub mod primitives {11	pub type CollectionId = u32;12	pub type ResourceId = u32;13	pub type NftId = u32;14	pub type BaseId = u32;15	pub type SlotId = u32;16	pub type PartId = u32;17	pub type ZIndex = u32;18}1920#[cfg(feature = "std")]21mod serialize {22    use core::convert::AsRef;23    use serde::ser::{self, Serialize};2425    pub mod vec {26        use super::*;2728        pub fn serialize<D, V, C>(value: &C, serializer: D) -> Result<D::Ok, D::Error>29        where30            D: ser::Serializer,31            V: Serialize,32            C: AsRef<[V]>,33        {34            value.as_ref().serialize(serializer)35        }36    }3738    pub mod opt_vec {39        use super::*;4041        pub fn serialize<D, V, C>(value: &Option<C>, serializer: D) -> Result<D::Ok, D::Error>42        where43            D: ser::Serializer,44            V: Serialize,45            C: AsRef<[V]>,46        {47            match value {48                Some(value) => super::vec::serialize(value, serializer),49                None => serializer.serialize_none()50            }51        }52    }53}5455/// Collection info.56#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]57#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]58#[cfg_attr(59	feature = "std",60	serde(61		bound = r#"62			AccountId: Serialize,63			BoundedString: AsRef<[u8]>,64			BoundedSymbol: AsRef<[u8]>65		"#66	)67)]68pub struct CollectionInfo<BoundedString, BoundedSymbol, AccountId> {69	/// Current bidder and bid price.70	pub issuer: AccountId,7172	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]73	pub metadata: BoundedString,74	pub max: Option<u32>,7576	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]77	pub symbol: BoundedSymbol,78	pub nfts_count: u32,79}8081#[derive(Encode, Decode, Eq, PartialEq, Copy, Clone, Debug, TypeInfo, MaxEncodedLen)]82#[cfg_attr(feature = "std", derive(Serialize))]83pub enum AccountIdOrCollectionNftTuple<AccountId> {84	AccountId(AccountId),85	CollectionAndNftTuple(CollectionId, NftId),86}8788/// Royalty information (recipient and amount)89#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]90#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]91pub struct RoyaltyInfo<AccountId, RoyaltyAmount> {92	/// Recipient (AccountId) of the royalty93    pub recipient: AccountId,94	/// Amount (Permill) of the royalty95    pub amount: RoyaltyAmount,96}9798/// Nft info.99#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]100#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]101#[cfg_attr(102	feature = "std",103	serde(104		bound = r#"105			AccountId: Serialize,106			RoyaltyAmount: Serialize,107			BoundedString: AsRef<[u8]>108		"#109	)110)]111pub struct NftInfo<AccountId, RoyaltyAmount, BoundedString> {112	/// The owner of the NFT, can be either an Account or a tuple (CollectionId, NftId)113	pub owner: AccountIdOrCollectionNftTuple<AccountId>,114	/// Royalty (optional)115	pub royalty: Option<RoyaltyInfo<AccountId, RoyaltyAmount>>,116117	/// Arbitrary data about an instance, e.g. IPFS hash118	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]119	pub metadata: BoundedString,120121	/// Equipped state122	pub equipped: bool,123	/// Pending state (if sent to NFT)124	pub pending: bool,125}126127#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]128#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]129pub struct NftChild {130	pub collection_id: CollectionId,131	pub nft_id: NftId132}133134#[cfg_attr(feature = "std", derive(Serialize))]135#[derive(Encode, Decode, PartialEq, TypeInfo)]136#[cfg_attr(137	feature = "std",138	serde(139		bound = r#"140			BoundedKey: AsRef<[u8]>,141			BoundedValue: AsRef<[u8]>142		"#143	)144)]145pub struct PropertyInfo<BoundedKey, BoundedValue>146{147	/// Key of the property148	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]149	pub key: BoundedKey,150151	/// Value of the property152	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]153	pub value: BoundedValue,154}155156#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]157#[cfg_attr(feature = "std", derive(Serialize))]158#[cfg_attr(159	feature = "std",160	serde(bound = "BoundedString: AsRef<[u8]>")161)]162pub struct BasicResource<BoundedString> {163	/// If the resource is Media, the base property is absent. Media src should be a URI like an164	/// IPFS hash.165	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]166	pub src: Option<BoundedString>,167168	/// Reference to IPFS location of metadata169	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]170	pub metadata: Option<BoundedString>,171172	/// Optional location or identier of license173	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]174	pub license: Option<BoundedString>,175176	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given177	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is178	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns179	/// another bird, showing the full render of one bird inside the other's inventory might be a180	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an181	/// image that is lighter and faster to load but representative of this resource.182	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]183	pub thumb: Option<BoundedString>,184}185186#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]187#[cfg_attr(feature = "std", derive(Serialize))]188#[cfg_attr(189	feature = "std",190	serde(191		bound = r#"192			BoundedString: AsRef<[u8]>,193			BoundedParts: AsRef<[PartId]>194		"#195	)196)]197pub struct ComposableResource<BoundedString, BoundedParts> {198	/// If a resource is composed, it will have an array of parts that compose it199	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]200	pub parts: BoundedParts,201202	/// A Base is uniquely identified by the combination of the word `base`, its minting block203	/// number, and user provided symbol during Base creation, glued by dashes `-`, e.g.204	/// base-4477293-kanaria_superbird.205	pub base: BaseId,206207	/// If the resource is Media, the base property is absent. Media src should be a URI like an208	/// IPFS hash.209	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]210	pub src: Option<BoundedString>,211212	/// Reference to IPFS location of metadata213	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]214	pub metadata: Option<BoundedString>,215216	/// If the resource has the slot property, it was designed to fit into a specific Base's slot.217	/// The baseslot will be composed of two dot-delimited values, like so:218	/// "base-4477293-kanaria_superbird.machine_gun_scope". This means: "This resource is219	/// compatible with the machine_gun_scope slot of base base-4477293-kanaria_superbird220221	/// Optional location or identier of license222	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]223	pub license: Option<BoundedString>,224225	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given226	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is227	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns228	/// another bird, showing the full render of one bird inside the other's inventory might be a229	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an230	/// image that is lighter and faster to load but representative of this resource.231	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]232	pub thumb: Option<BoundedString>,233}234235#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]236#[cfg_attr(feature = "std", derive(Serialize))]237#[cfg_attr(238	feature = "std",239	serde(bound = "BoundedString: AsRef<[u8]>")240)]241pub struct SlotResource<BoundedString> {242	/// A Base is uniquely identified by the combination of the word `base`, its minting block243	/// number, and user provided symbol during Base creation, glued by dashes `-`, e.g.244	/// base-4477293-kanaria_superbird.245	pub base: BaseId,246247	/// If the resource is Media, the base property is absent. Media src should be a URI like an248	/// IPFS hash.249	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]250	pub src: Option<BoundedString>,251252	/// Reference to IPFS location of metadata253	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]254	pub metadata: Option<BoundedString>,255256	/// If the resource has the slot property, it was designed to fit into a specific Base's slot.257	/// The baseslot will be composed of two dot-delimited values, like so:258	/// "base-4477293-kanaria_superbird.machine_gun_scope". This means: "This resource is259	/// compatible with the machine_gun_scope slot of base base-4477293-kanaria_superbird260	pub slot: SlotId,261262	/// The license field, if present, should contain a link to a license (IPFS or static HTTP263	/// url), or an identifier, like RMRK_nocopy or ipfs://ipfs/someHashOfLicense.264	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]265	pub license: Option<BoundedString>,266267	/// If the resource has the thumb property, this will be a URI to a thumbnail of the given268	/// resource. For example, if we have a composable NFT like a Kanaria bird, the resource is269	/// complex and too detailed to show in a search-results page or a list. Also, if a bird owns270	/// another bird, showing the full render of one bird inside the other's inventory might be a271	/// bit of a strain on the browser. For this reason, the thumb value can contain a URI to an272	/// image that is lighter and faster to load but representative of this resource.273	#[cfg_attr(feature = "std", serde(with = "serialize::opt_vec"))]274	pub thumb: Option<BoundedString>,275}276277#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]278#[cfg_attr(feature = "std", derive(Serialize))]279#[cfg_attr(280	feature = "std",281	serde(282		bound = r#"283			BoundedString: AsRef<[u8]>,284			BoundedParts: AsRef<[PartId]>285		"#286	)287)]288pub enum ResourceTypes<BoundedString, BoundedParts> {289	Basic(BasicResource<BoundedString>),290	Composable(ComposableResource<BoundedString, BoundedParts>),291	Slot(SlotResource<BoundedString>),292}293294295#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]296#[cfg_attr(feature = "std", derive(Serialize))]297#[cfg_attr(298	feature = "std",299	serde(300		bound = r#"301			BoundedResource: AsRef<[u8]>,302			BoundedString: AsRef<[u8]>,303			BoundedParts: AsRef<[PartId]>304		"#305	)306)]307pub struct ResourceInfo<BoundedResource, BoundedString, BoundedParts> {308	/// id is a 5-character string of reasonable uniqueness.309	/// The combination of base ID and resource id should be unique across the entire RMRK310	/// ecosystem which311	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]312	pub id: BoundedResource,313314	/// Resource315	pub resource: ResourceTypes<BoundedString, BoundedParts>,316317	/// If resource is sent to non-rootowned NFT, pending will be false and need to be accepted318	pub pending: bool,319320	/// If resource removal request is sent by non-rootowned NFT, pending will be true and need to be accepted321	pub pending_removal: bool,322}323324#[cfg_attr(feature = "std", derive(PartialEq, Eq, Serialize))]325#[derive(Encode, Decode, Debug, TypeInfo, MaxEncodedLen)]326#[cfg_attr(327	feature = "std",328	serde(329		bound = r#"330			AccountId: Serialize,331			BoundedString: AsRef<[u8]>332		"#333	)334)]335pub struct BaseInfo<AccountId, BoundedString> {336	/// Original creator of the Base337	pub issuer: AccountId,338339	/// Specifies how an NFT should be rendered, ie "svg"340	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]341	pub base_type: BoundedString,342343	/// User provided symbol during Base creation344	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]345	pub symbol: BoundedString,346}347348#[cfg_attr(feature = "std", derive(Serialize))]349#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]350#[cfg_attr(351	feature = "std",352	serde(bound = "BoundedString: AsRef<[u8]>")353)]354pub struct FixedPart<BoundedString> {355	pub id: PartId,356	pub z: ZIndex,357358	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]359	pub src: BoundedString,360}361362#[cfg_attr(feature = "std", derive(Serialize))]363#[derive(Encode, Decode, Debug, Default, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]364#[cfg_attr(365	feature = "std",366	serde(bound = "BoundedCollectionList: AsRef<[CollectionId]>")367)]368pub enum EquippableList<BoundedCollectionList> {369	All,370	#[default] Empty,371	Custom(372		#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]373		BoundedCollectionList374	),375}376377#[cfg_attr(feature = "std", derive(Serialize))]378#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]379#[cfg_attr(380	feature = "std",381	serde(382		bound = r#"383			BoundedString: AsRef<[u8]>,384			BoundedCollectionList: AsRef<[CollectionId]>385		"#386	)387)]388pub struct SlotPart<BoundedString, BoundedCollectionList> {389	pub id: PartId,390	pub equippable: EquippableList<BoundedCollectionList>,391392	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]393	pub src: BoundedString,394395	pub z: ZIndex,396}397398#[cfg_attr(feature = "std", derive(Serialize))]399#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]400#[cfg_attr(401	feature = "std",402	serde(403		bound = r#"404			BoundedString: AsRef<[u8]>,405			BoundedCollectionList: AsRef<[CollectionId]>406		"#407	)408)]409pub enum PartType<BoundedString, BoundedCollectionList> {410	FixedPart(FixedPart<BoundedString>),411	SlotPart(SlotPart<BoundedString, BoundedCollectionList>),412}413414#[cfg_attr(feature = "std", derive(Eq, Serialize))]415#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq)]416#[cfg_attr(417	feature = "std",418	serde(419		bound = r#"420			BoundedString: AsRef<[u8]>,421			PropertyList: AsRef<[ThemeProperty<BoundedString>]>,422		"#423	)424)]425pub struct Theme<BoundedString, PropertyList> {426	/// Name of the theme427	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]428	pub name: BoundedString,429430	/// Theme properties431	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]432	pub properties: PropertyList,433	/// Inheritability434	pub inherit: bool,435}436437#[cfg_attr(feature = "std", derive(Eq, Serialize))]438#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq)]439#[cfg_attr(440	feature = "std",441	serde(bound = "BoundedString: AsRef<[u8]>")442)]443pub struct ThemeProperty<BoundedString> {444	/// Key of the property445	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]446	pub key: BoundedString,447448	/// Value of the property449	#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]450	pub value: BoundedString,451}
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -38,10 +38,10 @@
                     keys: Option<Vec<Vec<u8>>>
                 ) -> Result<Vec<Property>, DispatchError> {
                     let keys = keys.map(
-                        |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)
+                        |keys| Common::bytes_keys_to_property_keys(keys)
                     ).transpose()?;
 
-                    pallet_common::Pallet::<Runtime>::filter_collection_properties(collection, keys)
+                    Common::filter_collection_properties(collection, keys)
                 }
 
                 fn token_properties(
@@ -50,7 +50,7 @@
                     keys: Option<Vec<Vec<u8>>>
                 ) -> Result<Vec<Property>, DispatchError> {
                     let keys = keys.map(
-                        |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)
+                        |keys| Common::bytes_keys_to_property_keys(keys)
                     ).transpose()?;
 
                     dispatch_unique_runtime!(collection.token_properties(token_id, keys))
@@ -61,10 +61,10 @@
                     keys: Option<Vec<Vec<u8>>>
                 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {
                     let keys = keys.map(
-                        |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)
+                        |keys| Common::bytes_keys_to_property_keys(keys)
                     ).transpose()?;
 
-                    pallet_common::Pallet::<Runtime>::filter_property_permissions(collection, keys)
+                    Common::filter_property_permissions(collection, keys)
                 }
 
                 fn token_data(
@@ -144,34 +144,30 @@
                 fn last_collection_idx() -> Result<RmrkCollectionId, DispatchError> {
                     Ok(RmrkCore::last_collection_idx())
                 }
+
                 fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {
-                    // TODO decide on displacement to palettes -- does RMRK belong there, spread across common and nonfungible?
                     use frame_support::BoundedVec;
                     use scale_info::prelude::string::String;
-                    use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
-
-                    // todo check if this is a rmrk standard collection? or simply trust and provide anyway?
-                    // client-is-always-right / enforce authority and order ?
+                    use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode}};
 
                     let collection_id = CollectionId(collection_id);
-                    let collection = <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_typed_nft_collection(collection_id, CollectionType::Regular)?;
+                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
+                        Ok(c) => c,
+                        Err(_) => return Ok(None),
+                    };
 
-                    let metadata = BoundedVec::try_from(
-                        <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_collection_property(collection_id, RmrkProperty::Metadata)?.into_inner()
-                    ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?;
-
-                    let nfts_count = (dispatch_unique_runtime!(collection_id.total_supply()) as Result<u32, DispatchError>)?; // todo? <Runtime>::total_supply(collection_id)
+                    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
 
                     Ok(Some(RmrkCollectionInfo {
                         issuer: collection.owner.clone(),
-                        metadata,
+                        metadata: RmrkCore::get_collection_property(collection_id, RmrkProperty::Metadata)?.decode_or_default(),
                         max: collection.limits.token_limit,
-                        symbol: BoundedVec::try_from(
-                            collection.token_prefix.clone().into_inner()
-                        ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?,
+                        symbol: collection.token_prefix.rebind(), // change
                         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;
@@ -179,6 +175,7 @@
 
                     let collection_id = CollectionId(collection_id);
                     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>)? {
                         Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
@@ -187,36 +184,23 @@
                         },
                         None => return Ok(None)
                     };
-
-                    // todo displace querying property key array to rmrk proxy pallet
-                    let keys = [
-                        RmrkProperty::RoyaltyInfo,
-                        RmrkProperty::Metadata,
-                        RmrkProperty::Equipped,
-                        // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
-                    ];
-
-                    let properties = keys.into_iter().map(
-                        |key| BoundedVec::try_from(
-                            <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, nft_id, key).unwrap().into_inner()
-                        ).unwrap()
-                    )
-                    .collect::<Vec<RmrkString>>();
-
+                    
                     let allowance = pallet_nonfungible::Allowance::<Runtime>::get((collection_id, nft_id));
 
                     Ok(Some(RmrkInstanceInfo {
                         owner: owner,
-                        //recipient: , // prop?
-                        royalty: properties[0].clone().decode_property().unwrap(),
-                        metadata: properties[1].clone(),
-                        equipped: properties[2].clone().decode_property().unwrap(),
+                        royalty: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::RoyaltyInfo)?.decode_or_default(),
+                        metadata: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::Metadata)?.decode_or_default(),
+                        equipped: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::Equipped)?.decode_or_default(),
                         pending: allowance.is_some(),
                     }))
                 }
+
                 fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {
                     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()); }
+
                     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)?
@@ -225,11 +209,14 @@
                             .collect::<Vec<_>>()
                     )
                 }
+
                 fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
                     use up_data_structs::mapping::TokenAddressMapping;
 
                     let collection_id = CollectionId(collection_id);
                     let nft_id = TokenId(nft_id);
+                    if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
+
                     let cross_account_id = CrossAccountId::from_eth(
                         EvmTokenAddressMapping::token_to_address(collection_id, nft_id)
                     );
@@ -243,21 +230,25 @@
                             .collect()
                     )
                 }
+
                 fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
-                    use frame_support::BoundedVec;
+                    use pallet_proxy_rmrk_core::misc::RmrkDecode;
 
                     let collection_id = CollectionId(collection_id);
-                    let properties = pallet_common::Pallet::<Runtime>::collection_properties(collection_id);
+                    if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); }
 
+                    let properties = Common::collection_properties(collection_id);
+
+                    // todo repeated code
                     return Ok(match filter_keys {
                         Some(keys) => {
-                            let keys = pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)?;
+                            let keys = Common::bytes_keys_to_property_keys(keys)?;
                             let properties = keys
                                 .into_iter()
                                 .filter_map(|key| {
                                     properties.get(&key).map(|value| RmrkPropertyInfo {
-                                        key: BoundedVec::try_from(key.into_inner()).unwrap(),
-                                        value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),
+                                        key: key.decode_or_default(),
+                                        value: value.decode_or_default(),
                                     })
                                 })
                                 .collect();
@@ -268,31 +259,35 @@
                             properties
                                 .iter()
                                 .filter_map(|(key, value)| Some(RmrkPropertyInfo {
-                                    key: BoundedVec::try_from(key.clone().into_inner()).unwrap(),
-                                    value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),
+                                    key: key.decode_or_default(),
+                                    value: value.decode_or_default(),
                                 }))
                                 .collect()
                         }
                     });
                 }
+
                 fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
                     use frame_support::BoundedVec;
+                    use pallet_proxy_rmrk_core::misc::RmrkDecode;
 
                     let collection_id = CollectionId(collection_id);
                     let token_id = TokenId(nft_id);
+                    if !RmrkCore::nft_exists(collection_id, token_id) { return Ok(Vec::new()); }
 
-		            let properties = pallet_nonfungible::Pallet::<Runtime>::token_properties((collection_id, token_id)); // todo look into usage of pallet_nonfungible
+		            let properties = Nonfungible::token_properties((collection_id, token_id));
+                    // todo look into this usage of pallet_nonfungible
 
                     // todo displace to a function? redundant code piece with collection props
                     return Ok(match filter_keys {
                         Some(keys) => {
-                            let keys = pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)?;
+                            let keys = Common::bytes_keys_to_property_keys(keys)?;
                             let properties = keys
                                 .into_iter()
                                 .filter_map(|key| {
                                     properties.get(&key).map(|value| RmrkPropertyInfo {
-                                        key: BoundedVec::try_from(key.into_inner()).unwrap(),
-                                        value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),
+                                        key: key.decode_or_default(),
+                                        value: value.decode_or_default(),
                                     })
                                 })
                                 .collect();
@@ -303,113 +298,76 @@
                             properties
                                 .iter()
                                 .filter_map(|(key, value)| Some(RmrkPropertyInfo {
-                                    key: BoundedVec::try_from(key.clone().into_inner()).unwrap(),
-                                    value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),
+                                    key: key.decode_or_default(),
+                                    value: value.decode_or_default(),
                                 }))
                                 .collect()
                         }
                     });
                 }
+
                 fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
                     use frame_support::BoundedVec;
-                    use pallet_proxy_rmrk_core::RmrkProperty;
+                    use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
 
                     let collection_id = CollectionId(collection_id);
-                    let nft_id = TokenId(nft_id);
-
-                    // let keys = [
-                    //     RmrkProperty::RoyaltyInfo,
-                    //     RmrkProperty::Metadata,
-                    //     RmrkProperty::Equipped,
-                    //     RmrkProperty::Pending,
-                    //     // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
-                    // ];
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }
 
-                    /*let resources = keys.into_iter().map(
-                        |key| BoundedVec::try_from(
-                            <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, nft_id, key).unwrap().into_inner()
-                        ).unwrap()
-                    )
-                    .collect::<Vec<RmrkString>>();*/
+                    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 {
-
+                        
                     }]*/))
                 }
+
                 fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {
                     todo!()
                 }
+
                 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};
+                    use pallet_proxy_rmrk_core::{
+                        RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode},
+                    };
 
                     let collection_id = CollectionId(base_id);
-                    let collection = <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_typed_nft_collection(collection_id, CollectionType::Base)?;
-                    // todo check prop for being a base
-
-                    // todo export to macro? redundancy
-                    let keys = [
-                        RmrkProperty::BaseType,
-                    ];
-
-                    let properties = keys.into_iter().map(
-                        |key| BoundedVec::try_from(
-                            <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_collection_property(collection_id, key).unwrap().into_inner()
-                        )
-                    )
-                    // todo not-a-rmrk-collection error
-                    .collect::<Result<Vec<_>, _>>()
-                    .map_err(|_| <pallet_proxy_rmrk_core::Error<Runtime>>::CollectionUnknown)?;
+                    let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
+                        Ok(c) => c,
+                        Err(_) => return Ok(None),
+                    };
 
                     Ok(Some(RmrkBaseInfo {
                         issuer: collection.owner.clone(),
-                        base_type: properties[0].clone(),
-                        symbol: BoundedVec::try_from(
-                            collection.token_prefix.clone().into_inner()
-                        ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?,
+                        base_type: RmrkCore::get_collection_property(collection_id, RmrkProperty::BaseType)?.decode_or_default(),
+                        symbol: collection.token_prefix.rebind(),
                     }))
                 }
+
                 fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
                     use frame_support::BoundedVec;
-                    use pallet_proxy_rmrk_core::{RmrkProperty, misc::{RmrkNft, RmrkDecode}};
+                    use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkNft, RmrkDecode}};
 
                     let collection_id = CollectionId(base_id);
-                    // todo check prop for being a base
+                    if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
 
                     let parts = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
                         .iter()
                         .filter_map(|token_id| {
-                            let nft_type = <pallet_nonfungible::TokenData<Runtime>>::get((collection_id, token_id))
-                                //.map_err(|_| ) // no need, tis a filter_map
-                                .unwrap()
-                                .rmrk_nft_type()?;
-
-                            // dislocate to rmrkproxycore and simply send an array of keys
-                            let keys = [
-                                //RmrkProperty::PartId)?,
-                                RmrkProperty::Src,
-                                RmrkProperty::ZIndex,
-                                RmrkProperty::EquippableList,
-                            ];
-
-                            let properties = keys.into_iter().map(
-                                |key| BoundedVec::try_from(
-                                    <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, *token_id, key).unwrap().into_inner()
-                                ).unwrap()
-                            ).collect::<Vec<RmrkString>>();
+                            let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
 
                             match nft_type {
                                 FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {
                                     id: token_id.0,
-                                    src: properties[0].clone().decode_property().unwrap(),
-                                    z: properties[1].clone().decode_property().unwrap(),
+                                    src: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::Src).unwrap().decode_or_default(),
+                                    z: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ZIndex).unwrap().decode_or_default(),
                                 })),
                                 SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {
                                     id: token_id.0,
-                                    src: properties[0].clone().decode_property().unwrap(),
-                                    z: properties[1].clone().decode_property().unwrap(),
-                                    equippable: properties[2].clone().decode_property().unwrap(),
+                                    src: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::Src).unwrap().decode_or_default(),
+                                    z: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ZIndex).unwrap().decode_or_default(),
+                                    equippable: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::EquippableList).unwrap().decode_or_default(),
                                 })),
                                 _ => None
                             }
@@ -418,6 +376,7 @@
 
                     Ok(parts)
                 }
+
                 fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
                     use frame_support::BoundedVec;
                     use pallet_proxy_rmrk_core::{RmrkProperty, misc::{RmrkNft, RmrkDecode}};
@@ -428,16 +387,11 @@
                     let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
                         .iter()
                         .filter_map(|token_id| {
-                            let nft_type = <pallet_nonfungible::TokenData<Runtime>>::get((collection_id, token_id))
-                                .unwrap()
-                                .rmrk_nft_type()?;
-
+                            let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
+                            
                             match nft_type {
                                 Theme => Some(
-                                    <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(
-                                        collection_id, *token_id, RmrkProperty::ThemeName
-                                    ).unwrap()
-                                    .into_inner()
+                                    RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ThemeName).unwrap().decode_or_default()
                                 ),
                                 _ => None
                             }
@@ -446,6 +400,7 @@
 
                     Ok(theme_names)
                 }
+
                 fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {
                     use frame_support::BoundedVec;
 
@@ -457,7 +412,7 @@
                     let themes = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
                         .iter()
                         .filter_map(|token_id| {
-                            let properties = pallet_nonfungible::Pallet::<Runtime>::token_properties((collection_id, token_id));
+                            let properties = Nonfungible::token_properties((collection_id, token_id));
 
                             // todo ping properties for "rmrk:nft-type"
                             // if none, skip, None
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -37,6 +37,7 @@
     "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",
     "testStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/**.test.ts",
     "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/properties.test.ts",
+    "testGraphs": "mocha --timeout 9999999 -r ts-node/register ./**/graphs.test.ts",
     "testMigrationStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",
     "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
     "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
modifiedtests/src/nesting/graphs.test.tsdiffbeforeafterboth
--- a/tests/src/nesting/graphs.test.ts
+++ b/tests/src/nesting/graphs.test.ts
@@ -32,10 +32,10 @@
   return collectionId;
 }
 
-describe('Graphs', () => {
+describe.skip('Graphs', () => {
   it('Ouroboros can\'t be created in a complex graph', async () => {
     await usingApi(async api => {
-      const alice = privateKey('//Alice');
+      const alice = privateKey('//alice');
       const collection = await buildComplexObjectGraph(api, alice);
 
       // to self
@@ -55,3 +55,197 @@
     });
   });
 });
+
+import type { EventRecord } from '@polkadot/types/interfaces';
+import type { GenericEventData } from '@polkadot/types';
+import type { Option, Bytes } from '@polkadot/types-codec';
+import type {
+    RmrkTypesCollectionInfo as Collection,
+    RmrkTypesNftInfo as Nft,
+    RmrkTypesResourceInfo as Resource,
+    RmrkTypesBaseInfo as Base,
+    RmrkTypesPartType as PartType,
+    RmrkTypesNftChild as NftChild,
+    RmrkTypesTheme as Theme,
+    RmrkTypesPropertyInfo as Property,
+} from '@polkadot/types/lookup';
+
+interface TxResult<T> {
+  success: boolean;
+  successData: T | null;
+}
+
+export function extractTxResult<T>(
+  events: EventRecord[],
+  expectSection: string,
+  expectMethod: string,
+  extractAction: (data: GenericEventData) => T
+): TxResult<T> {
+  let success = false;
+  let successData = null;
+  events.forEach(({event: {data, method, section}}) => {
+    //console.log(expectSection + " "+ " " + section + " " + expectMethod + " " + method)
+    if (method == 'ExtrinsicSuccess') {
+      success = true;
+    } else if ((expectSection == section) && (expectMethod == method)) {
+      successData = extractAction(data);
+    }
+  });
+  const result: TxResult<T> = {
+      success,
+      successData,
+  };
+  return result;
+}
+
+export function extractRmrkCoreTxResult<T>(
+  events: EventRecord[],
+  expectMethod: string,
+  extractAction: (data: GenericEventData) => T
+): TxResult<T> {
+  return extractTxResult(events, 'rmrkCore', expectMethod, extractAction);
+}
+
+export async function expectTxFailure(expectedError: RegExp, promise: Promise<any>) {
+  await expect(promise).to.be.rejectedWith(expectedError);
+}
+
+export async function getCollectionsCount(api: ApiPromise): Promise<number> {
+  return (await api.rpc.rmrk.lastCollectionIdx()).toNumber();
+}
+
+export async function getCollection(api: ApiPromise, id: number): Promise<Option<Collection>> {
+  return api.rpc.rmrk.collectionById(id);
+}
+
+export async function createCollection(
+  api: ApiPromise,
+  issuerUri: string,
+  metadata: string,
+  max: number | null,
+  symbol: string
+): Promise<number> {
+  let collectionId = 0;
+
+  const oldCollectionCount = await getCollectionsCount(api);
+  const maxOptional = max ? max.toString() : null;
+  console.log(maxOptional)
+  console.log('right above me')
+
+  const issuer = privateKey(issuerUri);
+  const tx = api.tx.rmrkCore.createCollection(metadata, maxOptional, symbol);
+  const events = await executeTransaction(api, issuer, tx);
+
+  const collectionResult = extractRmrkCoreTxResult(
+    events, 'CollectionCreated', (data) => {
+      return parseInt(data[1].toString(), 10)
+    }
+  );
+  expect(collectionResult.success, 'Error: unable to create a collection').to.be.true;
+  const newCollectionCount = await getCollectionsCount(api);
+  expect(newCollectionCount).to.be.equal(oldCollectionCount + 1, 'Error: NFT collection count should increase');
+
+  collectionId = collectionResult.successData ?? 0;
+  
+  console.log(collectionId);
+
+  const collectionOption = await getCollection(api, collectionId);
+
+  expect(collectionOption.isSome, 'Error: unable to fetch created NFT collection').to.be.true;
+
+  const collection = collectionOption.unwrap();
+
+  expect(collection.metadata.toUtf8()).to.be.equal(metadata, "Error: Invalid NFT collection metadata");
+  console.log(collection.max, max)
+  expect(collection.max.isSome).to.be.equal(max !== null, "Error: Invalid NFT collection max");
+
+  if (collection.max.isSome) {
+      expect(collection.max.unwrap().toNumber()).to.be.equal(max, "Error: Invalid NFT collection max");
+  }
+  expect(collection.symbol.toUtf8()).to.be.equal(symbol, "Error: Invalid NFT collection's symbol");
+  expect(collection.nftsCount.toNumber()).to.be.equal(0, "Error: NFT collection shoudn't have any tokens");
+  expect(collection.issuer.toString()).to.be.equal(issuer.address, "Error: Invalid NFT collection issuer");
+
+  return collectionId;
+}
+
+export async function deleteCollection(
+  api: ApiPromise,
+  issuerUri: string,
+  collectionId: string
+): Promise<number> {
+  const issuer = privateKey(issuerUri);
+  const tx = api.tx.rmrkCore.destroyCollection(collectionId);
+  const events = await executeTransaction(api, issuer, tx);
+
+  const collectionTxResult = extractRmrkCoreTxResult(
+      events,
+      "CollectionDestroy",
+      (data) => {
+      return parseInt(data[1].toString(), 10);
+      }
+  );
+  expect(collectionTxResult.success, 'Error: Unable to delete NFT collection').to.be.true;
+
+  const collection = await getCollection(
+      api,
+      parseInt(collectionId, 10)
+  );
+  expect(collection.isEmpty, 'Error: NFT collection should be deleted').to.be.true;
+
+  return 0;
+}
+
+describe('Something', () => {
+  const alice = '//Alice';
+  const bob = "//Bob";
+
+  it('create NFT collection', async () => {
+    await usingApi(async api => {
+      await createCollection(api, alice, 'test-metadata', 42, 'test-symbol');
+      //console.log((await api.rpc.rmrk.base(3)).toHuman());
+    });
+  });
+
+  it('create NFT collection without token limit', async () => {
+    await usingApi(async api => {
+      await createCollection(api, alice, 'no-limit-metadata', null, 'no-limit-symbol');
+    });
+  });
+
+  it("Delete NFT collection", async () => {
+    await usingApi(async api => {
+      await createCollection(
+        api,
+        alice,
+        "test-metadata",
+        null,
+        "test-symbol"
+      ).then(async (collectionId) => {
+        await deleteCollection(api, alice, collectionId.toString());
+      });
+    });
+  });
+
+  it("[Negative] delete non-existing NFT collection", async () => {
+    await usingApi(async api => {
+      const tx = deleteCollection(api, alice, "99999");
+      await expectTxFailure(/rmrkCore.CollectionUnknown/, tx);
+    });
+  });
+
+  it("[Negative] delete not an owner NFT collection", async () => {
+    await usingApi(async api => {
+      await createCollection(
+        api,
+        alice,
+        "test-metadata",
+        null,
+        "test-symbol"
+      ).then(async (collectionId) => {
+        const tx = deleteCollection(api, bob, collectionId.toString());
+        await expectTxFailure(/uniques.NoPermission/, tx);
+      });
+    });
+  });
+});
\ No newline at end of file