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
22use sp_std::vec::Vec;22use sp_std::vec::Vec;
23use up_data_structs::*;23use up_data_structs::*;
24use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};24use pallet_common::{Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations};
25use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle};25use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};
26use pallet_evm::account::CrossAccountId;26use pallet_evm::account::CrossAccountId;
2727
28pub use pallet::*;28pub use pallet::*;
396 Ok(collection)396 Ok(collection)
397 }397 }
398
399 // should this even be here, might displace it to common/nonfungible -- but they did not need it, only rmrk does
400 pub fn collection_exists(collection_id: CollectionId) -> bool {
401 <pallet_common::CollectionById<T>>::contains_key(collection_id)
402 }
403
404 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {
405 <TokenData<T>>::contains_key((collection_id, nft_id))
406 }
398407
399 pub fn get_collection_property(collection_id: CollectionId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {408 pub fn get_collection_property(collection_id: CollectionId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
400 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)409 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)
414 Ok(collection_type)423 Ok(collection_type)
415 }424 }
425
426 pub fn ensure_collection_type(collection_id: CollectionId, collection_type: CollectionType) -> DispatchResult {
427 let actual_type = Self::get_collection_type(collection_id)?;
428 ensure!(actual_type == collection_type, <CommonError<T>>::NoPermission);
429
430 Ok(())
431 }
416432
417 pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {433 pub fn get_nft_property(collection_id: CollectionId, nft_id: TokenId, key: RmrkProperty) -> Result<PropertyValue, DispatchError> {
418 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))434 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
423 Ok(nft_property)439 Ok(nft_property)
424 }440 }
441
442 pub fn get_nft_type(collection_id: CollectionId, token_id: TokenId) -> Result<NftType, DispatchError> {
443 <TokenData<T>>::get((collection_id, token_id))
444 .unwrap()
445 .rmrk_nft_type()
446 .ok_or(<Error<T>>::NoAvailableNftId.into())
447 }
425448
426 pub fn check_collection_type(collection_id: CollectionId, collection_type: CollectionType) -> DispatchResult {449 pub fn ensure_nft_type(collection_id: CollectionId, token_id: TokenId, nft_type: NftType) -> DispatchResult {
427 let actual_type = Self::get_collection_type(collection_id)?;450 let actual_type = Self::get_nft_type(collection_id, token_id)?;
428 ensure!(actual_type == collection_type, <CommonError<T>>::NoPermission);451 ensure!(actual_type == nft_type, <CommonError<T>>::NoPermission);
429452
430 Ok(())453 Ok(())
431 }454 }
434 collection_id: CollectionId,457 collection_id: CollectionId,
435 collection_type: CollectionType458 collection_type: CollectionType
436 ) -> Result<NonfungibleHandle<T>, DispatchError> {459 ) -> Result<NonfungibleHandle<T>, DispatchError> {
437 Self::check_collection_type(collection_id, collection_type)?;460 Self::ensure_collection_type(collection_id, collection_type)?;
438461
439 Self::get_nft_collection(collection_id)462 Self::get_nft_collection(collection_id)
440 }463 }
modifiedpallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth
82 }82 }
83}83}
8484
85pub trait RmrkDecode<T: Decode> {85pub trait RmrkDecode<T: Decode + Default, S> {
86 fn decode_property(&self) -> Option<T>;86 fn decode_or_default(&self) -> T;
87}87}
8888
89impl<T: Decode> RmrkDecode<T> for RmrkString {89impl<T: Decode + Default, S> RmrkDecode<T, S> for BoundedVec<u8, S> {
90 fn decode_property(&self) -> Option<T> { // todo access runtime errors? // but then rmrk_nft_type must have it too90 fn decode_or_default(&self) -> T {
91 let mut value = self.as_slice();91 let mut value = self.as_slice();
9292
93 T::decode(&mut value).ok()93 T::decode(&mut value).unwrap_or_default()
94 }94 }
95}95}
96
97pub trait RmrkRebind<T, S> {
98 fn rebind(&self) -> BoundedVec<u8, S>;
99}
100
101impl<T, S> RmrkRebind<T, S> for BoundedVec<u8, T> where BoundedVec<u8, S>: TryFrom<Vec<u8>> {
102 fn rebind(&self) -> BoundedVec<u8, S> {
103 BoundedVec::<u8, S>::try_from(
104 self.clone().into_inner()
105 ).unwrap_or_default()
106 }
107}
96108
97#[derive(Encode, Decode, PartialEq, Eq)]109#[derive(Encode, Decode, PartialEq, Eq)]
98pub enum CollectionType {110pub enum CollectionType {
modifiedprimitives/data-structs/src/rmrk.rsdiffbeforeafterboth
360}360}
361361
362#[cfg_attr(feature = "std", derive(Serialize))]362#[cfg_attr(feature = "std", derive(Serialize))]
363#[derive(Encode, Decode, Debug, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]363#[derive(Encode, Decode, Debug, Default, TypeInfo, Clone, PartialEq, Eq, MaxEncodedLen)]
364#[cfg_attr(364#[cfg_attr(
365 feature = "std",365 feature = "std",
366 serde(bound = "BoundedCollectionList: AsRef<[CollectionId]>")366 serde(bound = "BoundedCollectionList: AsRef<[CollectionId]>")
367)]367)]
368pub enum EquippableList<BoundedCollectionList> {368pub enum EquippableList<BoundedCollectionList> {
369 All,369 All,
370 Empty,370 #[default] Empty,
371 Custom(371 Custom(
372 #[cfg_attr(feature = "std", serde(with = "serialize::vec"))]372 #[cfg_attr(feature = "std", serde(with = "serialize::vec"))]
373 BoundedCollectionList373 BoundedCollectionList
modifiedruntime/common/src/runtime_apis.rsdiffbeforeafterboth
38 keys: Option<Vec<Vec<u8>>>38 keys: Option<Vec<Vec<u8>>>
39 ) -> Result<Vec<Property>, DispatchError> {39 ) -> Result<Vec<Property>, DispatchError> {
40 let keys = keys.map(40 let keys = keys.map(
41 |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)41 |keys| Common::bytes_keys_to_property_keys(keys)
42 ).transpose()?;42 ).transpose()?;
4343
44 pallet_common::Pallet::<Runtime>::filter_collection_properties(collection, keys)44 Common::filter_collection_properties(collection, keys)
45 }45 }
4646
47 fn token_properties(47 fn token_properties(
50 keys: Option<Vec<Vec<u8>>>50 keys: Option<Vec<Vec<u8>>>
51 ) -> Result<Vec<Property>, DispatchError> {51 ) -> Result<Vec<Property>, DispatchError> {
52 let keys = keys.map(52 let keys = keys.map(
53 |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)53 |keys| Common::bytes_keys_to_property_keys(keys)
54 ).transpose()?;54 ).transpose()?;
5555
56 dispatch_unique_runtime!(collection.token_properties(token_id, keys))56 dispatch_unique_runtime!(collection.token_properties(token_id, keys))
61 keys: Option<Vec<Vec<u8>>>61 keys: Option<Vec<Vec<u8>>>
62 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {62 ) -> Result<Vec<PropertyKeyPermission>, DispatchError> {
63 let keys = keys.map(63 let keys = keys.map(
64 |keys| pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)64 |keys| Common::bytes_keys_to_property_keys(keys)
65 ).transpose()?;65 ).transpose()?;
6666
67 pallet_common::Pallet::<Runtime>::filter_property_permissions(collection, keys)67 Common::filter_property_permissions(collection, keys)
68 }68 }
6969
70 fn token_data(70 fn token_data(
146 }146 }
147
147 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> {
148 // TODO decide on displacement to palettes -- does RMRK belong there, spread across common and nonfungible?
149 use frame_support::BoundedVec;149 use frame_support::BoundedVec;
150 use scale_info::prelude::string::String;150 use scale_info::prelude::string::String;
151 use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};151 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode}};
152
153 // todo check if this is a rmrk standard collection? or simply trust and provide anyway?
154 // client-is-always-right / enforce authority and order ?
155152
156 let collection_id = CollectionId(collection_id);153 let collection_id = CollectionId(collection_id);
157 let collection = <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_typed_nft_collection(collection_id, CollectionType::Regular)?;154 let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
158
159 let metadata = BoundedVec::try_from(155 Ok(c) => c,
160 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_collection_property(collection_id, RmrkProperty::Metadata)?.into_inner()
161 ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?;156 Err(_) => return Ok(None),
157 };
162158
163 let nfts_count = (dispatch_unique_runtime!(collection_id.total_supply()) as Result<u32, DispatchError>)?; // todo? <Runtime>::total_supply(collection_id)159 let nfts_count = (dispatch_unique_runtime!(collection_id.total_supply()) as Result<u32, DispatchError>)?;
160 //<Runtime as up_rpc::UniqueApi>::total_supply(collection_id); // todo can't find UniqueApi with disabled default features
164161
165 Ok(Some(RmrkCollectionInfo {162 Ok(Some(RmrkCollectionInfo {
166 issuer: collection.owner.clone(),163 issuer: collection.owner.clone(),
167 metadata,164 metadata: RmrkCore::get_collection_property(collection_id, RmrkProperty::Metadata)?.decode_or_default(),
168 max: collection.limits.token_limit,165 max: collection.limits.token_limit,
169 symbol: BoundedVec::try_from(166 symbol: collection.token_prefix.rebind(), // change
170 collection.token_prefix.clone().into_inner()
171 ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?,
172 nfts_count167 nfts_count
173 }))168 }))
174 }169 }
179175
180 let collection_id = CollectionId(collection_id);176 let collection_id = CollectionId(collection_id);
181 let nft_id = TokenId(nft_by_id);177 let nft_id = TokenId(nft_by_id);
178 if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(None); }
182179
183 let owner = match (dispatch_unique_runtime!(collection_id.token_owner(nft_id)) as Result<Option<CrossAccountId>, DispatchError>)? {180 let owner = match (dispatch_unique_runtime!(collection_id.token_owner(nft_id)) as Result<Option<CrossAccountId>, DispatchError>)? {
184 Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {181 Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
188 None => return Ok(None)185 None => return Ok(None)
189 };186 };
190187
191 // todo displace querying property key array to rmrk proxy pallet
192 let keys = [
193 RmrkProperty::RoyaltyInfo,
194 RmrkProperty::Metadata,
195 RmrkProperty::Equipped,
196 // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
197 ];
198
199 let properties = keys.into_iter().map(
200 |key| BoundedVec::try_from(
201 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, nft_id, key).unwrap().into_inner()
202 ).unwrap()
203 )
204 .collect::<Vec<RmrkString>>();
205
206 let allowance = pallet_nonfungible::Allowance::<Runtime>::get((collection_id, nft_id));188 let allowance = pallet_nonfungible::Allowance::<Runtime>::get((collection_id, nft_id));
207189
208 Ok(Some(RmrkInstanceInfo {190 Ok(Some(RmrkInstanceInfo {
209 owner: owner,191 owner: owner,
210 //recipient: , // prop?
211 royalty: properties[0].clone().decode_property().unwrap(),192 royalty: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::RoyaltyInfo)?.decode_or_default(),
212 metadata: properties[1].clone(),193 metadata: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::Metadata)?.decode_or_default(),
213 equipped: properties[2].clone().decode_property().unwrap(),194 equipped: RmrkCore::get_nft_property(collection_id, nft_id, RmrkProperty::Equipped)?.decode_or_default(),
214 pending: allowance.is_some(),195 pending: allowance.is_some(),
215 }))196 }))
216 }197 }
198
217 fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {199 fn account_tokens(account_id: AccountId, collection_id: RmrkCollectionId) -> Result<Vec<RmrkNftId>, DispatchError> {
218 let cross_account_id = CrossAccountId::from_sub(account_id);200 let cross_account_id = CrossAccountId::from_sub(account_id);
219 let collection_id = CollectionId(collection_id);201 let collection_id = CollectionId(collection_id);
202 if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); }
203
220 Ok(204 Ok(
221 (dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id)) as Result<Vec<TokenId>, DispatchError>)?205 (dispatch_unique_runtime!(collection_id.account_tokens(cross_account_id)) as Result<Vec<TokenId>, DispatchError>)?
230215
231 let collection_id = CollectionId(collection_id);216 let collection_id = CollectionId(collection_id);
232 let nft_id = TokenId(nft_id);217 let nft_id = TokenId(nft_id);
218 if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
219
233 let cross_account_id = CrossAccountId::from_eth(220 let cross_account_id = CrossAccountId::from_eth(
234 EvmTokenAddressMapping::token_to_address(collection_id, nft_id)221 EvmTokenAddressMapping::token_to_address(collection_id, nft_id)
245 }232 }
233
246 fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {234 fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
247 use frame_support::BoundedVec;235 use pallet_proxy_rmrk_core::misc::RmrkDecode;
248236
249 let collection_id = CollectionId(collection_id);237 let collection_id = CollectionId(collection_id);
238 if !RmrkCore::collection_exists(collection_id) { return Ok(Vec::new()); }
239
250 let properties = pallet_common::Pallet::<Runtime>::collection_properties(collection_id);240 let properties = Common::collection_properties(collection_id);
251241
242 // todo repeated code
252 return Ok(match filter_keys {243 return Ok(match filter_keys {
253 Some(keys) => {244 Some(keys) => {
254 let keys = pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)?;245 let keys = Common::bytes_keys_to_property_keys(keys)?;
255 let properties = keys246 let properties = keys
256 .into_iter()247 .into_iter()
257 .filter_map(|key| {248 .filter_map(|key| {
258 properties.get(&key).map(|value| RmrkPropertyInfo {249 properties.get(&key).map(|value| RmrkPropertyInfo {
259 key: BoundedVec::try_from(key.into_inner()).unwrap(),250 key: key.decode_or_default(),
260 value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),251 value: value.decode_or_default(),
261 })252 })
262 })253 })
263 .collect();254 .collect();
268 properties259 properties
269 .iter()260 .iter()
270 .filter_map(|(key, value)| Some(RmrkPropertyInfo {261 .filter_map(|(key, value)| Some(RmrkPropertyInfo {
271 key: BoundedVec::try_from(key.clone().into_inner()).unwrap(),262 key: key.decode_or_default(),
272 value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),263 value: value.decode_or_default(),
273 }))264 }))
274 .collect()265 .collect()
275 }266 }
276 });267 });
277 }268 }
269
278 fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {270 fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
279 use frame_support::BoundedVec;271 use frame_support::BoundedVec;
272 use pallet_proxy_rmrk_core::misc::RmrkDecode;
280273
281 let collection_id = CollectionId(collection_id);274 let collection_id = CollectionId(collection_id);
282 let token_id = TokenId(nft_id);275 let token_id = TokenId(nft_id);
276 if !RmrkCore::nft_exists(collection_id, token_id) { return Ok(Vec::new()); }
283277
284 let properties = pallet_nonfungible::Pallet::<Runtime>::token_properties((collection_id, token_id)); // todo look into usage of pallet_nonfungible278 let properties = Nonfungible::token_properties((collection_id, token_id));
279 // todo look into this usage of pallet_nonfungible
285280
286 // todo displace to a function? redundant code piece with collection props281 // todo displace to a function? redundant code piece with collection props
287 return Ok(match filter_keys {282 return Ok(match filter_keys {
288 Some(keys) => {283 Some(keys) => {
289 let keys = pallet_common::Pallet::<Runtime>::bytes_keys_to_property_keys(keys)?;284 let keys = Common::bytes_keys_to_property_keys(keys)?;
290 let properties = keys285 let properties = keys
291 .into_iter()286 .into_iter()
292 .filter_map(|key| {287 .filter_map(|key| {
293 properties.get(&key).map(|value| RmrkPropertyInfo {288 properties.get(&key).map(|value| RmrkPropertyInfo {
294 key: BoundedVec::try_from(key.into_inner()).unwrap(),289 key: key.decode_or_default(),
295 value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),290 value: value.decode_or_default(),
296 })291 })
297 })292 })
298 .collect();293 .collect();
303 properties298 properties
304 .iter()299 .iter()
305 .filter_map(|(key, value)| Some(RmrkPropertyInfo {300 .filter_map(|(key, value)| Some(RmrkPropertyInfo {
306 key: BoundedVec::try_from(key.clone().into_inner()).unwrap(),301 key: key.decode_or_default(),
307 value: BoundedVec::try_from(value.clone().into_inner()).unwrap(),302 value: value.decode_or_default(),
308 }))303 }))
309 .collect()304 .collect()
310 }305 }
311 });306 });
312 }307 }
308
313 fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {309 fn nft_resources(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceInfo>, DispatchError> {
314 use frame_support::BoundedVec;310 use frame_support::BoundedVec;
315 use pallet_proxy_rmrk_core::RmrkProperty;311 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
316312
317 let collection_id = CollectionId(collection_id);313 let collection_id = CollectionId(collection_id);
314 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Resource).is_err() { return Ok(Vec::new()); }
315
318 let nft_id = TokenId(nft_id);316 let nft_id = TokenId(nft_id);
319317 if RmrkCore::ensure_nft_type(collection_id, nft_id, NftType::Resource).is_err() { return Ok(Vec::new()); }
320 // let keys = [
321 // RmrkProperty::RoyaltyInfo,
322 // RmrkProperty::Metadata,
323 // RmrkProperty::Equipped,
324 // RmrkProperty::Pending,
325 // // ?? "rmrk:recipient", "rmrk:nft-type", "rmrk:resource-collection", "rmrk:resource-priorities"
326 // ];
327
328 /*let resources = keys.into_iter().map(
329 |key| BoundedVec::try_from(
330 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, nft_id, key).unwrap().into_inner()
331 ).unwrap()
332 )
333 .collect::<Vec<RmrkString>>();*/
334318
335 Ok(Vec::new(/*[RmrkResourceInfo {319 Ok(Vec::new(/*[RmrkResourceInfo {
336320
343 use frame_support::BoundedVec;329 use frame_support::BoundedVec;
344 use scale_info::prelude::string::String;330 use scale_info::prelude::string::String;
345 use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};331 use pallet_proxy_rmrk_core::{
332 RmrkProperty, misc::{CollectionType, RmrkRebind, RmrkDecode},
333 };
346334
347 let collection_id = CollectionId(base_id);335 let collection_id = CollectionId(base_id);
348 let collection = <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_typed_nft_collection(collection_id, CollectionType::Base)?;336 let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
349 // todo check prop for being a base
350
351 // todo export to macro? redundancy
352 let keys = [
353 RmrkProperty::BaseType,
354 ];
355
356 let properties = keys.into_iter().map(337 Ok(c) => c,
357 |key| BoundedVec::try_from(
358 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_collection_property(collection_id, key).unwrap().into_inner()338 Err(_) => return Ok(None),
359 )
360 )
361 // todo not-a-rmrk-collection error
362 .collect::<Result<Vec<_>, _>>()
363 .map_err(|_| <pallet_proxy_rmrk_core::Error<Runtime>>::CollectionUnknown)?;339 };
364340
365 Ok(Some(RmrkBaseInfo {341 Ok(Some(RmrkBaseInfo {
366 issuer: collection.owner.clone(),342 issuer: collection.owner.clone(),
367 base_type: properties[0].clone(),343 base_type: RmrkCore::get_collection_property(collection_id, RmrkProperty::BaseType)?.decode_or_default(),
368 symbol: BoundedVec::try_from(344 symbol: collection.token_prefix.rebind(),
369 collection.token_prefix.clone().into_inner()
370 ).map_err(|_| <pallet_common::Error<Runtime>>::PropertyKeyIsTooLong)?,
371 }))345 }))
372 }346 }
347
373 fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {348 fn base_parts(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
374 use frame_support::BoundedVec;349 use frame_support::BoundedVec;
375 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{RmrkNft, RmrkDecode}};350 use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, RmrkNft, RmrkDecode}};
376351
377 let collection_id = CollectionId(base_id);352 let collection_id = CollectionId(base_id);
378 // todo check prop for being a base353 if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
379354
380 let parts = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?355 let parts = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
381 .iter()356 .iter()
382 .filter_map(|token_id| {357 .filter_map(|token_id| {
383 let nft_type = <pallet_nonfungible::TokenData<Runtime>>::get((collection_id, token_id))358 let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
384 //.map_err(|_| ) // no need, tis a filter_map
385 .unwrap()
386 .rmrk_nft_type()?;
387
388 // dislocate to rmrkproxycore and simply send an array of keys
389 let keys = [
390 //RmrkProperty::PartId)?,
391 RmrkProperty::Src,
392 RmrkProperty::ZIndex,
393 RmrkProperty::EquippableList,
394 ];
395
396 let properties = keys.into_iter().map(
397 |key| BoundedVec::try_from(
398 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(collection_id, *token_id, key).unwrap().into_inner()
399 ).unwrap()
400 ).collect::<Vec<RmrkString>>();
401359
402 match nft_type {360 match nft_type {
403 FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {361 FixedPart => Some(RmrkPartType::FixedPart(RmrkFixedPart {
404 id: token_id.0,362 id: token_id.0,
405 src: properties[0].clone().decode_property().unwrap(),363 src: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::Src).unwrap().decode_or_default(),
406 z: properties[1].clone().decode_property().unwrap(),364 z: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ZIndex).unwrap().decode_or_default(),
407 })),365 })),
408 SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {366 SlotPart => Some(RmrkPartType::SlotPart(RmrkSlotPart {
409 id: token_id.0,367 id: token_id.0,
410 src: properties[0].clone().decode_property().unwrap(),368 src: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::Src).unwrap().decode_or_default(),
411 z: properties[1].clone().decode_property().unwrap(),369 z: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ZIndex).unwrap().decode_or_default(),
412 equippable: properties[2].clone().decode_property().unwrap(),370 equippable: RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::EquippableList).unwrap().decode_or_default(),
413 })),371 })),
414 _ => None372 _ => None
415 }373 }
428 let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?387 let theme_names = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
429 .iter()388 .iter()
430 .filter_map(|token_id| {389 .filter_map(|token_id| {
431 let nft_type = <pallet_nonfungible::TokenData<Runtime>>::get((collection_id, token_id))390 let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
432 .unwrap()391
433 .rmrk_nft_type()?;
434
435 match nft_type {392 match nft_type {
436 Theme => Some(393 Theme => Some(
437 <pallet_proxy_rmrk_core::Pallet<Runtime>>::get_nft_property(394 RmrkCore::get_nft_property(collection_id, *token_id, RmrkProperty::ThemeName).unwrap().decode_or_default()
438 collection_id, *token_id, RmrkProperty::ThemeName
439 ).unwrap()
440 .into_inner()
441 ),395 ),
442 _ => None396 _ => None
443 }397 }
457 let themes = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?412 let themes = (dispatch_unique_runtime!(collection_id.collection_tokens()) as Result<Vec<TokenId>, DispatchError>)?
458 .iter()413 .iter()
459 .filter_map(|token_id| {414 .filter_map(|token_id| {
460 let properties = pallet_nonfungible::Pallet::<Runtime>::token_properties((collection_id, token_id));415 let properties = Nonfungible::token_properties((collection_id, token_id));
461416
462 // todo ping properties for "rmrk:nft-type"417 // todo ping properties for "rmrk:nft-type"
463 // if none, skip, None418 // if none, skip, None
modifiedtests/package.jsondiffbeforeafterboth
37 "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",37 "testUnnesting": "mocha --timeout 9999999 -r ts-node/register ./**/unnest.test.ts",
38 "testStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/**.test.ts",38 "testStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/**.test.ts",
39 "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/properties.test.ts",39 "testProperties": "mocha --timeout 9999999 -r ts-node/register ./**/properties.test.ts",
40 "testGraphs": "mocha --timeout 9999999 -r ts-node/register ./**/graphs.test.ts",
40 "testMigrationStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",41 "testMigrationStructure": "mocha --timeout 9999999 -r ts-node/register ./**/nesting/migration-check.test.ts",
41 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",42 "testAddCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/addCollectionAdmin.test.ts",
42 "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",43 "testSetSchemaVersion": "mocha --timeout 9999999 -r ts-node/register ./**/setSchemaVersion.test.ts",
modifiedtests/src/nesting/graphs.test.tsdiffbeforeafterboth
32 return collectionId;32 return collectionId;
33}33}
3434
35describe('Graphs', () => {35describe.skip('Graphs', () => {
36 it('Ouroboros can\'t be created in a complex graph', async () => {36 it('Ouroboros can\'t be created in a complex graph', async () => {
37 await usingApi(async api => {37 await usingApi(async api => {
38 const alice = privateKey('//Alice');38 const alice = privateKey('//alice');
39 const collection = await buildComplexObjectGraph(api, alice);39 const collection = await buildComplexObjectGraph(api, alice);
4040
41 // to self41 // to self
56 });56 });
57});57});
5858
59import type { EventRecord } from '@polkadot/types/interfaces';
60import type { GenericEventData } from '@polkadot/types';
61import type { Option, Bytes } from '@polkadot/types-codec';
62import type {
63 RmrkTypesCollectionInfo as Collection,
64 RmrkTypesNftInfo as Nft,
65 RmrkTypesResourceInfo as Resource,
66 RmrkTypesBaseInfo as Base,
67 RmrkTypesPartType as PartType,
68 RmrkTypesNftChild as NftChild,
69 RmrkTypesTheme as Theme,
70 RmrkTypesPropertyInfo as Property,
71} from '@polkadot/types/lookup';
72
73interface TxResult<T> {
74 success: boolean;
75 successData: T | null;
76}
77
78export function extractTxResult<T>(
79 events: EventRecord[],
80 expectSection: string,
81 expectMethod: string,
82 extractAction: (data: GenericEventData) => T
83): TxResult<T> {
84 let success = false;
85 let successData = null;
86 events.forEach(({event: {data, method, section}}) => {
87 //console.log(expectSection + " "+ " " + section + " " + expectMethod + " " + method)
88 if (method == 'ExtrinsicSuccess') {
89 success = true;
90 } else if ((expectSection == section) && (expectMethod == method)) {
91 successData = extractAction(data);
92 }
93 });
94 const result: TxResult<T> = {
95 success,
96 successData,
97 };
98 return result;
99}
100
101export function extractRmrkCoreTxResult<T>(
102 events: EventRecord[],
103 expectMethod: string,
104 extractAction: (data: GenericEventData) => T
105): TxResult<T> {
106 return extractTxResult(events, 'rmrkCore', expectMethod, extractAction);
107}
108
109export async function expectTxFailure(expectedError: RegExp, promise: Promise<any>) {
110 await expect(promise).to.be.rejectedWith(expectedError);
111}
112
113export async function getCollectionsCount(api: ApiPromise): Promise<number> {
114 return (await api.rpc.rmrk.lastCollectionIdx()).toNumber();
115}
116
117export async function getCollection(api: ApiPromise, id: number): Promise<Option<Collection>> {
118 return api.rpc.rmrk.collectionById(id);
119}
120
121export async function createCollection(
122 api: ApiPromise,
123 issuerUri: string,
124 metadata: string,
125 max: number | null,
126 symbol: string
127): Promise<number> {
128 let collectionId = 0;
129
130 const oldCollectionCount = await getCollectionsCount(api);
131 const maxOptional = max ? max.toString() : null;
132 console.log(maxOptional)
133 console.log('right above me')
134
135 const issuer = privateKey(issuerUri);
136 const tx = api.tx.rmrkCore.createCollection(metadata, maxOptional, symbol);
137 const events = await executeTransaction(api, issuer, tx);
138
139 const collectionResult = extractRmrkCoreTxResult(
140 events, 'CollectionCreated', (data) => {
141 return parseInt(data[1].toString(), 10)
142 }
143 );
144 expect(collectionResult.success, 'Error: unable to create a collection').to.be.true;
145 const newCollectionCount = await getCollectionsCount(api);
146 expect(newCollectionCount).to.be.equal(oldCollectionCount + 1, 'Error: NFT collection count should increase');
147
148 collectionId = collectionResult.successData ?? 0;
149
150 console.log(collectionId);
151
152 const collectionOption = await getCollection(api, collectionId);
153
154 expect(collectionOption.isSome, 'Error: unable to fetch created NFT collection').to.be.true;
155
156 const collection = collectionOption.unwrap();
157
158 expect(collection.metadata.toUtf8()).to.be.equal(metadata, "Error: Invalid NFT collection metadata");
159 console.log(collection.max, max)
160 expect(collection.max.isSome).to.be.equal(max !== null, "Error: Invalid NFT collection max");
161
162 if (collection.max.isSome) {
163 expect(collection.max.unwrap().toNumber()).to.be.equal(max, "Error: Invalid NFT collection max");
164 }
165 expect(collection.symbol.toUtf8()).to.be.equal(symbol, "Error: Invalid NFT collection's symbol");
166 expect(collection.nftsCount.toNumber()).to.be.equal(0, "Error: NFT collection shoudn't have any tokens");
167 expect(collection.issuer.toString()).to.be.equal(issuer.address, "Error: Invalid NFT collection issuer");
168
169 return collectionId;
170}
171
172export async function deleteCollection(
173 api: ApiPromise,
174 issuerUri: string,
175 collectionId: string
176): Promise<number> {
177 const issuer = privateKey(issuerUri);
178 const tx = api.tx.rmrkCore.destroyCollection(collectionId);
179 const events = await executeTransaction(api, issuer, tx);
180
181 const collectionTxResult = extractRmrkCoreTxResult(
182 events,
183 "CollectionDestroy",
184 (data) => {
185 return parseInt(data[1].toString(), 10);
186 }
187 );
188 expect(collectionTxResult.success, 'Error: Unable to delete NFT collection').to.be.true;
189
190 const collection = await getCollection(
191 api,
192 parseInt(collectionId, 10)
193 );
194 expect(collection.isEmpty, 'Error: NFT collection should be deleted').to.be.true;
195
196 return 0;
197}
198
199describe('Something', () => {
200 const alice = '//Alice';
201 const bob = "//Bob";
202
203 it('create NFT collection', async () => {
204 await usingApi(async api => {
205 await createCollection(api, alice, 'test-metadata', 42, 'test-symbol');
206 //console.log((await api.rpc.rmrk.base(3)).toHuman());
207 });
208 });
209
210 it('create NFT collection without token limit', async () => {
211 await usingApi(async api => {
212 await createCollection(api, alice, 'no-limit-metadata', null, 'no-limit-symbol');
213 });
214 });
215
216 it("Delete NFT collection", async () => {
217 await usingApi(async api => {
218 await createCollection(
219 api,
220 alice,
221 "test-metadata",
222 null,
223 "test-symbol"
224 ).then(async (collectionId) => {
225 await deleteCollection(api, alice, collectionId.toString());
226 });
227 });
228 });
229
230 it("[Negative] delete non-existing NFT collection", async () => {
231 await usingApi(async api => {
232 const tx = deleteCollection(api, alice, "99999");
233 await expectTxFailure(/rmrkCore.CollectionUnknown/, tx);
234 });
235 });
236
237 it("[Negative] delete not an owner NFT collection", async () => {
238 await usingApi(async api => {
239 await createCollection(
240 api,
241 alice,
242 "test-metadata",
243 null,
244 "test-symbol"
245 ).then(async (collectionId) => {
246 const tx = deleteCollection(api, bob, collectionId.toString());
247 await expectTxFailure(/uniques.NoPermission/, tx);
248 });
249 });
250 });
251});