difftreelog
feat(rmrk-rpc) nft resources and priorities
in: master
6 files changed
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth486 }486 }487 }487 }488488489 // 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 {489 pub fn collection_exists(collection_id: CollectionId) -> bool {491 <pallet_common::CollectionById<T>>::contains_key(collection_id)490 <CollectionHandle<T>>::try_get(collection_id).is_ok()492 }491 }493492494 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {493 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {pallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth26 }26 }27}27}2829pub trait RmrkRebind<T, S> {30 fn rebind(&self) -> BoundedVec<u8, S>;31}3233impl<T, S> RmrkRebind<T, S> for BoundedVec<u8, T> where BoundedVec<u8, S>: TryFrom<Vec<u8>> {34 fn rebind(&self) -> BoundedVec<u8, S> {35 BoundedVec::<u8, S>::try_from(36 self.clone().into_inner()37 ).unwrap_or_default()38 }39}402841#[derive(Encode, Decode, PartialEq, Eq)]29#[derive(Encode, Decode, PartialEq, Eq)]42pub enum CollectionType {30pub enum CollectionType {primitives/data-structs/src/lib.rsdiffbeforeafterboth49 },49 },50 NftChild as RmrkNftChild, AccountIdOrCollectionNftTuple as RmrkAccountIdOrCollectionNftTuple,50 NftChild as RmrkNftChild, AccountIdOrCollectionNftTuple as RmrkAccountIdOrCollectionNftTuple,51 FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart, EquippableList as RmrkEquippableList,51 FixedPart as RmrkFixedPart, SlotPart as RmrkSlotPart, EquippableList as RmrkEquippableList,52 BasicResource as RmrkBasicResource, ComposableResource as RmrkComposableResource, SlotResource as RmrkSlotResource,52};53};535454mod bounded;55mod bounded;925 }926 }926}927}927928928pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;929pub type RmrkCollectionInfo<AccountId> =929pub type RmrkCollectionInfo<AccountId> =930 CollectionInfo<RmrkString, RmrkCollectionSymbol, AccountId>;930 CollectionInfo<RmrkString, RmrkCollectionSymbol, AccountId>;931pub type RmrkInstanceInfo<AccountId> = NftInfo<AccountId, Permill, RmrkString>;931pub type RmrkInstanceInfo<AccountId> = NftInfo<AccountId, Permill, RmrkString>;932pub type RmrkResourceInfo = ResourceInfo<932pub type RmrkResourceInfo = ResourceInfo<933 BoundedVec<u8, RmrkResourceSymbolLimit>,933 RmrkBoundedResource,934 RmrkString,934 RmrkString,935 BoundedVec<RmrkPartId, RmrkPartsLimit>,935 RmrkBoundedParts,936>;936>;937pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;938pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;939pub type RmrkPropertyInfo = PropertyInfo<RmrkKeyString, RmrkValueString>;937pub type RmrkPropertyInfo = PropertyInfo<RmrkKeyString, RmrkValueString>;940pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;938pub type RmrkBaseInfo<AccountId> = BaseInfo<AccountId, RmrkString>;941pub type RmrkPartType =939pub type RmrkPartType =942 PartType<RmrkString, BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>>;940 PartType<RmrkString, BoundedVec<RmrkCollectionId, RmrkMaxCollectionsEquippablePerPart>>;943pub type RmrkThemeProperty = ThemeProperty<RmrkString>;941pub type RmrkThemeProperty = ThemeProperty<RmrkString>;944pub type RmrkTheme = Theme<RmrkString, Vec<RmrkThemeProperty>>;942pub type RmrkTheme = Theme<RmrkString, Vec<RmrkThemeProperty>>;943944pub type RmrkCollectionSymbol = BoundedVec<u8, RmrkCollectionSymbolLimit>;945pub type RmrkKeyString = BoundedVec<u8, RmrkKeyLimit>;946pub type RmrkValueString = BoundedVec<u8, RmrkValueLimit>;947948type RmrkBoundedResource = BoundedVec<u8, RmrkResourceSymbolLimit>;949type RmrkBoundedParts = BoundedVec<RmrkPartId, RmrkPartsLimit>;945950946pub type RmrkRpcString = Vec<u8>;951pub type RmrkRpcString = Vec<u8>;947pub type RmrkThemeName = RmrkRpcString;952pub type RmrkThemeName = RmrkRpcString;primitives/data-structs/src/rmrk.rsdiffbeforeafterboth154 pub value: BoundedValue,154 pub value: BoundedValue,155}155}156156157#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]157#[derive(Encode, Decode, Default, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]158#[cfg_attr(feature = "std", derive(Serialize))]158#[cfg_attr(feature = "std", derive(Serialize))]159#[cfg_attr(159#[cfg_attr(160 feature = "std",160 feature = "std",275 pub thumb: Option<BoundedString>,275 pub thumb: Option<BoundedString>,276}276}277277278#[derive(Encode, Decode, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]278#[derive(Encode, Decode, Derivative, Eq, PartialEq, Clone, Debug, TypeInfo, MaxEncodedLen)]279#[cfg_attr(feature = "std", derive(Serialize))]279#[cfg_attr(feature = "std", derive(Serialize))]280#[cfg_attr(280#[cfg_attr(281 feature = "std",281 feature = "std",286 "#286 "#287 )287 )288)]288)]289#[derivative(Default(bound=""))]289pub enum ResourceTypes<BoundedString, BoundedParts> {290pub enum ResourceTypes<BoundedString: Default, BoundedParts> {291 #[derivative(Default)]290 Basic(BasicResource<BoundedString>),292 Basic(BasicResource<BoundedString>),291 Composable(ComposableResource<BoundedString, BoundedParts>),293 Composable(ComposableResource<BoundedString, BoundedParts>),292 Slot(SlotResource<BoundedString>),294 Slot(SlotResource<BoundedString>),305 "#307 "#306 )308 )307)]309)]308pub struct ResourceInfo<BoundedResource, BoundedString, BoundedParts> {310pub struct ResourceInfo<BoundedResource, BoundedString: Default, BoundedParts> {309 /// id is a 5-character string of reasonable uniqueness.311 /// id is a 5-character string of reasonable uniqueness.310 /// The combination of base ID and resource id should be unique across the entire RMRK312 /// The combination of base ID and resource id should be unique across the entire RMRK311 /// ecosystem which313 /// ecosystem whichruntime/common/src/runtime_apis.rsdiffbeforeafterboth146 }146 }147147148 fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {148 fn collection_by_id(collection_id: RmrkCollectionId) -> Result<Option<RmrkCollectionInfo<AccountId>>, DispatchError> {149 use frame_support::BoundedVec;150 use scale_info::prelude::string::String;151 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode}};149 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkDecode}};152150153 let collection_id = CollectionId(collection_id);151 let collection_id = CollectionId(collection_id);154 let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {152 let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {155 Ok(c) => c,153 Ok(c) => c,156 Err(_) => return Ok(None),154 Err(_) => return Ok(None),157 };155 };158156159 let nfts_count = (dispatch_unique_runtime!(collection_id.total_supply()) as Result<u32, DispatchError>)?;157 let nfts_count = dispatch_unique_runtime!(collection_id.total_supply())?;160 //<Runtime as up_rpc::UniqueApi>::total_supply(collection_id); // todo can't find UniqueApi with disabled default features161158162 Ok(Some(RmrkCollectionInfo {159 Ok(Some(RmrkCollectionInfo {163 issuer: collection.owner.clone(),160 issuer: collection.owner.clone(),164 metadata: RmrkCore::get_collection_property(collection_id, RmrkProperty::Metadata)?.decode_or_default(),161 metadata: RmrkCore::get_collection_property(collection_id, RmrkProperty::Metadata)?.decode_or_default(),165 max: collection.limits.token_limit,162 max: collection.limits.token_limit,166 symbol: collection.token_prefix.rebind(), // change163 symbol: collection.token_prefix.decode_or_default(),167 nfts_count164 nfts_count168 }))165 }))169 }166 }170167171 fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {168 fn nft_by_id(collection_id: RmrkCollectionId, nft_by_id: RmrkNftId) -> Result<Option<RmrkInstanceInfo<AccountId>>, DispatchError> {172 use frame_support::BoundedVec;173 use up_data_structs::mapping::TokenAddressMapping;169 use up_data_structs::mapping::TokenAddressMapping;174 use pallet_proxy_rmrk_core::{RmrkProperty, misc::RmrkDecode};170 use pallet_proxy_rmrk_core::{RmrkProperty, misc::RmrkDecode};175171176 let collection_id = CollectionId(collection_id);172 let collection_id = CollectionId(collection_id);177 let nft_id = TokenId(nft_by_id);173 let nft_id = TokenId(nft_by_id);178 if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }174 if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }179175180 let owner = match (dispatch_unique_runtime!(collection_id.token_owner(nft_id)) as Result<Option<CrossAccountId>, DispatchError>)? {176 let owner = match dispatch_unique_runtime!(collection_id.token_owner(nft_id))? {181 Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {177 Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {182 Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),178 Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),183 None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())179 None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())197 }193 }198194199 fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {195 fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {196 use pallet_proxy_rmrk_core::misc::CollectionType;197200 let cross_account_id = CrossAccountId::from_sub(account_id);198 let cross_account_id = CrossAccountId::from_sub(account_id);201 let collection_id = CollectionId(collection_id);199 let collection_id = CollectionId(collection_id);202 if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); }200 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }203201204 Ok(202 Ok(205 (dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id)) as Result<Vec<TokenId>, DispatchError>)?203 dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id))?206 //<Runtime as up_rpc::UniqueApi<Block, CrossAccountId, AccountId>>::account_tokens(collection_id, cross_account_id)?207 .into_iter()204 .into_iter()208 .map(|token| token.0)205 .map(|token| token.0)209 .collect::<Vec<_>>()206 .collect()210 )207 )211 }208 }212209277273278 fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {274 fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {279 use frame_support::BoundedVec;275 use frame_support::BoundedVec;280 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};276 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};281277282 let collection_id = CollectionId(collection_id);278 let collection_id = CollectionId(collection_id);283 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }279 if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); } // todo make sure the collection type doesn't matter284280285 let nft_id = TokenId(nft_id);281 let nft_id = TokenId(nft_id);286 if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }282 if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }287283284 let resource_collection_id: CollectionId = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourceCollection)285 .unwrap()286 .decode_or_default();288 Ok(Vec::new(/*[RmrkResourceInfo {287 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }289288290 }]*/))289 let resources = pallet_nonfungible::TokenProperties::<Runtime>::iter_prefix((resource_collection_id,))290 .filter_map(|(resource_id, properties)| Some(RmrkResourceInfo {291 id: BoundedVec::default(), // todo ResourceId property292 pending: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::PendingResourceAccept).unwrap().decode_or_default(),293 pending_removal: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::PendingResourceRemoval).unwrap().decode_or_default(),294 resource: RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::ResourceType).unwrap().decode_or_default(),/* {295 RmrkResourceTypes::Basic(resource) => RmrkResourceTypes::Basic(),/*(RmrkBasicResource {296 src: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Src).unwrap().decode_or_default(),297 metadata: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Metadata).unwrap().decode_or_default(),298 license: RmrkCore::get_nft_property_inner(properties, RmrkProperty::License).unwrap().decode_or_default(),299 thumb: RmrkCore::get_nft_property_inner(properties, RmrkProperty::Thumb).unwrap().decode_or_default(),300 },*///BasicResource<BoundedString>)301 _ => todo!(), //RmrkResourceTypes::Composable(ComposableResource<BoundedString, BoundedParts>),302 //RmrkResourceTypes::Slot(SlotResource<BoundedString>),303 },*/304 }))305 .collect();306307 Ok(resources)291 }308 }292309293 fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {310 fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {311 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};312313 let collection_id = CollectionId(collection_id);294 todo!()314 if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); } // todo ensure the collection type doesn't matter315316 let nft_id = TokenId(nft_id);317 if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }318319 /*let resource_collection_id: CollectionId = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourceCollection)320 .unwrap()321 .decode_or_default();322 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }323324 let resources = pallet_nonfungible::TokenProperties::<Runtime>::iter_prefix((resource_collection_id,))325 .filter_map(|(resource_id, properties)| Some((326 resource_id, // ResourceId property327 RmrkCore::get_nft_property(resource_collection_id, resource_id, RmrkProperty::Priority).unwrap().decode_or_default(),328 )))329 .collect()330 .sort_by_key(|(_, index)| *index)331 .into_iter().map(|(resource_id, _)| resource_id)*/332 let priorities = RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::ResourcePriorities)?.decode_or_default();333334 Ok(priorities)295 }335 }296336297 fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {337 fn base(base_id: RmrkBaseId) -> Result<Option<RmrkBaseInfo<AccountId>>, DispatchError> {298 use frame_support::BoundedVec;299 use scale_info::prelude::string::String;300 use pallet_proxy_rmrk_core::{338 use pallet_proxy_rmrk_core::{301 RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode},339 RmrkProperty, misc::{CollectionType, RmrkDecode},302 };340 };303341304 let collection_id = CollectionId(base_id);342 let collection_id = CollectionId(base_id);310 Ok(Some(RmrkBaseInfo {348 Ok(Some(RmrkBaseInfo {311 issuer: collection.owner.clone(),349 issuer: collection.owner.clone(),312 base_type: RmrkCore::get_collection_property(collection_id, RmrkProperty::BaseType)?.decode_or_default(),350 base_type: RmrkCore::get_collection_property(collection_id, RmrkProperty::BaseType)?.decode_or_default(),313 symbol: collection.token_prefix.rebind(),351 symbol: collection.token_prefix.decode_or_default(),314 }))352 }))315 }353 }316354317 fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {355 fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {318 use frame_support::BoundedVec;319 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};356 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, RmrkDecode}};320357321 let collection_id = CollectionId(base_id);358 let collection_id = CollectionId(base_id);322 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }359 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }323360324 let parts = (dispatch_unique_runtime!(collection_id.collection_tokens()))?361 let parts = dispatch_unique_runtime!(collection_id.collection_tokens())?325 .into_iter()362 .into_iter()326 .filter_map(|token_id| {363 .filter_map(|token_id| {327 let nft_type = RmrkCore::get_nft_type(collection_id, token_id).ok()?;364 let nft_type = RmrkCore::get_nft_type(collection_id, token_id).ok()?;347 }384 }348385349 fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {386 fn theme_names(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {350 use frame_support::BoundedVec;351 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkDecode}};387 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkDecode}};352388353 let collection_id = CollectionId(base_id);389 let collection_id = CollectionId(base_id);354 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {390 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {355 return Ok(Vec::new());391 return Ok(Vec::new());356 }392 }357393358 let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()))?394 let theme_names = dispatch_unique_runtime!(collection_id.collection_tokens())?359 .iter()395 .iter()360 .filter_map(|token_id| {396 .filter_map(|token_id| {361 let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();397 let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();373 }409 }374410375 fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {411 fn theme(base_id: RmrkBaseId, theme_name: RmrkThemeName, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Option<RmrkTheme>, DispatchError> {376 use frame_support::BoundedVec;377 use pallet_proxy_rmrk_core::{412 use pallet_proxy_rmrk_core::{378 RmrkProperty,413 RmrkProperty,379 misc::{CollectionType, NftType, RmrkDecode}414 misc::{CollectionType, NftType, RmrkDecode}384 return Ok(None);419 return Ok(None);385 }420 }386421387 let theme_info = (dispatch_unique_runtime!(collection_id.collection_tokens()))?422 let theme_info = dispatch_unique_runtime!(collection_id.collection_tokens())?388 .into_iter()423 .into_iter()389 .find_map(|token_id| {424 .find_map(|token_id| {390 RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;425 RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Theme).ok()?;tests/src/nesting/properties.test.tsdiffbeforeafterboth711 });711 });712 });712 });713713714 it('Forbids changing/deleting properties of a token if the property is permanent (constant)', async () => {714 it('Forbids changing/deleting properties of a token if the property is permanent (immutable)', async () => {715 await usingApi(async api => {715 await usingApi(async api => {716 let i = -1;716 let i = -1;717 for (const permission of constitution) {717 for (const permission of constitution) {