git.delta.rocks / unique-network / refs/commits / 73992bf599ce

difftreelog

doc: rmrk proxies

Farhad Hakimov2022-07-15parent: #12c8c8f.patch.diff
in: master

8 files changed

modifiedpallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
2// This file is part of Unique Network.
3
4// Unique Network is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Unique Network is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
1use sp_std::vec;17use sp_std::vec;
218
modifiedpallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
1616
17//! # RMRK Core Proxy Pallet
18//!
19//! A pallet used as proxy for RMRK Core (<https://rmrk-team.github.io/rmrk-substrate/#/pallets/rmrk-core>).
20//!
21//! - [`Config`]
22//! - [`Call`]
23//! - [`Pallet`]
24//!
25//! ## Overview
26//!
27//! The RMRK Core Proxy pallet mirrors the functionality of RMRK Core,
28//! binding its externalities to Unique's own underlying structure.
29//! It is purposed to mimic RMRK Core exactly, allowing seamless integrations
30//! of solutions based on RMRK.
31//!
32//! RMRK Core itself contains essential functionality for RMRK's nested and
33//! multi-resourced NFTs.
34//!
35//! *Note*, that while RMRK itself is subject to active development and restructuring,
36//! the proxy may be caught temporarily out of date.
37//!
38//! ### What is RMRK?
39//!
40//! RMRK is a set of NFT standards which compose several "NFT 2.0 lego" primitives.
41//! Putting these legos together allows a user to create NFT systems of arbitrary complexity.
42//!
43//! Meaning, RMRK NFTs are dynamic, able to nest into each other and form a hierarchy,
44//! make use of specific changeable and partially shared metadata in the form of resources,
45//! and more.
46//!
47//! Visit RMRK documentation and repositories to learn more:
48//! - Docs: <https://docs.rmrk.app/getting-started/>
49//! - FAQ: <https://coda.io/@rmrk/faq>
50//! - Substrate code repository: <https://github.com/rmrk-team/rmrk-substrate>
51//! - RMRK spec repository: <https://github.com/rmrk-team/rmrk-spec>
52//!
53//! ## Proxy Implementation
54//!
55//! An external user is supposed to be able to utilize this proxy as they would
56//! utilize RMRK, and get exactly the same results. Normally, Unique transactions
57//! are off-limits to RMRK collections and tokens, and vice versa. However,
58//! the information stored on chain can be freely interpreted by storage reads and RPCs.
59//!
60//! ### ID Mapping
61//!
62//! RMRK's collections' IDs are counted independently of Unique's and start at 0.
63//! Note that tokens' IDs still start at 1.
64//! The collections themselves, as well as tokens, are stored as Unique collections,
65//! and thus RMRK IDs are mapped to Unique IDs (but not vice versa).
66//!
67//! ### External/Internal Collection Insulation
68//!
69//! A Unique transaction cannot target collections purposed for RMRK,
70//! and they are flagged as `external` to specify that. On the other hand,
71//! due to the mapping, RMRK transactions and RPCs simply cannot reach Unique collections.
72//!
73//! ### Native Properties
74//!
75//! Many of RMRK's native parameters are stored as scoped properties of a collection
76//! or an NFT on the chain. Scoped properties are prefixed with `rmrk:`, where `:`
77//! is an unacceptable symbol in user-defined proeprties, which, along with other safeguards,
78//! makes them impossible to tamper with.
79//!
80//! ### Collection and NFT Types
81//!
82//! RMRK introduces the concept of a Base, which is a catalgoue of Parts,
83//! possible components of an NFT. Due to its similarity with the functionality
84//! of a token collection, a Base is stored and handled as one, and the Base's Parts and Themes
85//! are the collection's NFTs. See [`CollectionType`](pallet_rmrk_core::misc::CollectionType) and
86//! [`NftType`](pallet_rmrk_core::misc::NftType).
87//!
88//! ## Interface
89//!
90//! ### Dispatchables
91//!
92//! - `create_collection` - Create a new collection of NFTs.
93//! - `destroy_collection` - Destroy a collection.
94//! - `change_collection_issuer` - Change the issuer of a collection.
95//! Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
96//! - `lock_collection` - "Lock" the collection and prevent new token creation. **Cannot be undone.**
97//! - `mint_nft` - Mint an NFT in a specified collection.
98//! - `burn_nft` - Burn an NFT, destroying it and its nested tokens.
99//! - `send` - Transfer an NFT from an account/NFT A to another account/NFT B.
100//! - `accept_nft` - Accept an NFT sent from another account to self or an owned NFT.
101//! - `reject_nft` - Reject an NFT sent from another account to self or owned NFT and **burn it**.
102//! - `accept_resource` - Accept the addition of a newly created pending resource to an existing NFT.
103//! - `accept_resource_removal` - Accept the removal of a removal-pending resource from an NFT.
104//! - `set_property` - Add or edit a custom user property of a token or a collection.
105//! - `set_priority` - Set a different order of resource priorities for an NFT.
106//! - `add_basic_resource` - Create and set/propose a basic resource for an NFT.
107//! - `add_composable_resource` - Create and set/propose a composable resource for an NFT.
108//! - `add_slot_resource` - Create and set/propose a slot resource for an NFT.
109//! - `remove_resource` - Remove and erase a resource from an NFT.
110
17#![cfg_attr(not(feature = "std"), no_std)]111#![cfg_attr(not(feature = "std"), no_std)]
18112
49143
50use RmrkProperty::*;144use RmrkProperty::*;
51145
146/// Maximum number of levels of depth in the token nesting tree.
52pub const NESTING_BUDGET: u32 = 5;147pub const NESTING_BUDGET: u32 = 5;
53148
54type PendingTarget = (CollectionId, TokenId);149type PendingTarget = (CollectionId, TokenId);
66 pub trait Config:161 pub trait Config:
67 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config162 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config
68 {163 {
164 /// Overarching event type.
69 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;165 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
166
167 /// The weight information of this pallet.
70 type WeightInfo: WeightInfo;168 type WeightInfo: WeightInfo;
71 }169 }
72170
171 /// Latest yet-unused collection ID.
73 #[pallet::storage]172 #[pallet::storage]
74 #[pallet::getter(fn collection_index)]173 #[pallet::getter(fn collection_index)]
75 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;174 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;
76175
176 /// Mapping from RMRK collection ID to Unique's.
77 #[pallet::storage]177 #[pallet::storage]
78 pub type UniqueCollectionId<T: Config> =178 pub type UniqueCollectionId<T: Config> =
79 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;179 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;
159259
160 #[pallet::error]260 #[pallet::error]
161 pub enum Error<T> {261 pub enum Error<T> {
162 /* Unique-specific events */262 /* Unique proxy-specific events */
263 /// Property of the type of RMRK collection could not be read successfully.
163 CorruptedCollectionType,264 CorruptedCollectionType,
164 NftTypeEncodeError,265 // NftTypeEncodeError,
266 /// Too many symbols supplied as the property key. The maximum is [256](up_data_structs::MAX_PROPERTY_KEY_LENGTH).
165 RmrkPropertyKeyIsTooLong,267 RmrkPropertyKeyIsTooLong,
268 /// Too many bytes supplied as the property value. The maximum is [32768](up_data_structs::MAX_PROPERTY_VALUE_LENGTH).
166 RmrkPropertyValueIsTooLong,269 RmrkPropertyValueIsTooLong,
270 /// Could not find a property by the supplied key.
167 RmrkPropertyIsNotFound,271 RmrkPropertyIsNotFound,
272 /// Something went wrong when decoding encoded data from the storage.
273 /// Perhaps, there was a wrong key supplied for the type, or the data was improperly stored.
168 UnableToDecodeRmrkData,274 UnableToDecodeRmrkData,
169275
170 /* RMRK compatible events */276 /* RMRK compatible events */
277 /// Only destroying collections without tokens is allowed.
171 CollectionNotEmpty,278 CollectionNotEmpty,
279 /// Could not find an ID for a collection. It is likely there were too many collections created on the chain.
172 NoAvailableCollectionId,280 NoAvailableCollectionId,
281 /// Token does not exist, or there is no suitable ID for it, likely too many tokens were created in a collection.
173 NoAvailableNftId,282 NoAvailableNftId,
283 /// Collection does not exist, has a wrong type, or does not map to a Unique ID.
174 CollectionUnknown,284 CollectionUnknown,
285 /// No permission to perform action.
175 NoPermission,286 NoPermission,
287 /// Token is marked as non-transferable, and thus cannot be transferred.
176 NonTransferable,288 NonTransferable,
289 /// Too many tokens created in the collection, no new ones are allowed.
177 CollectionFullOrLocked,290 CollectionFullOrLocked,
291 /// No such resource found.
178 ResourceDoesntExist,292 ResourceDoesntExist,
293 /// If an NFT is sent to a descendant, that would form a nesting loop, an ouroboros.
294 /// Sending to self is redundant.
179 CannotSendToDescendentOrSelf,295 CannotSendToDescendentOrSelf,
296 /// Not the target owner of the sent NFT.
180 CannotAcceptNonOwnedNft,297 CannotAcceptNonOwnedNft,
298 /// Not the target owner of the sent NFT.
181 CannotRejectNonOwnedNft,299 CannotRejectNonOwnedNft,
300 /// NFT was not sent and is not pending.
182 CannotRejectNonPendingNft,301 CannotRejectNonPendingNft,
302 /// Resource is not pending for the operation.
183 ResourceNotPending,303 ResourceNotPending,
304 /// Could not find an ID for the resource. Is is likely there were too many resources created on an NFT.
184 NoAvailableResourceId,305 NoAvailableResourceId,
185 }306 }
186307
187 #[pallet::call]308 #[pallet::call]
188 impl<T: Config> Pallet<T> {309 impl<T: Config> Pallet<T> {
189 /// Create a collection310 // todo :refactor replace every collection_id with rmrk_collection_id (and nft_id) in arguments for uniformity?
311
312 /// Create a new collection of NFTs.
313 ///
314 /// # Permissions:
315 /// * Anyone - will be assigned as the issuer of the collection.
316 ///
317 /// # Arguments:
318 /// - `metadata`: Metadata describing the collection, e.g. IPFS hash. Cannot be changed.
319 /// - `max`: Optional maximum number of tokens.
320 /// - `symbol`: UTF-8 string with token prefix, by which to represent the token in wallets and UIs.
321 /// Analogous to Unique's [`token_prefix`](up_data_structs::Collection). Cannot be changed.
190 #[transactional]322 #[transactional]
191 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]323 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]
192 pub fn create_collection(324 pub fn create_collection(
226 T::CrossAccountId::from_sub(sender.clone()),358 T::CrossAccountId::from_sub(sender.clone()),
227 data,359 data,
228 [360 [
229 Self::rmrk_property(Metadata, &metadata)?,361 Self::encode_rmrk_property(Metadata, &metadata)?,
230 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,362 Self::encode_rmrk_property(CollectionType, &misc::CollectionType::Regular)?,
231 ]363 ]
232 .into_iter(),364 .into_iter(),
233 )?;365 )?;
237369
238 <PalletCommon<T>>::set_scoped_collection_property(370 <PalletCommon<T>>::set_scoped_collection_property(
239 unique_collection_id,371 unique_collection_id,
240 PropertyScope::Rmrk,372 RMRK_SCOPE,
241 Self::rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,373 Self::encode_rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,
242 )?;374 )?;
243375
244 <CollectionIndex<T>>::mutate(|n| *n += 1);376 <CollectionIndex<T>>::mutate(|n| *n += 1);
251 Ok(())383 Ok(())
252 }384 }
253385
254 /// destroy collection386 /// Destroy a collection.
387 ///
388 /// Only empty collections can be destroyed. If it has any tokens, they must be burned first.
389 ///
390 /// # Permissions:
391 /// * Collection issuer
392 ///
393 /// # Arguments:
394 /// - `collection_id`: RMRK ID of the collection to destroy.
255 #[transactional]395 #[transactional]
256 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]396 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]
257 pub fn destroy_collection(397 pub fn destroy_collection(
278 Ok(())418 Ok(())
279 }419 }
280420
281 /// Change the issuer of a collection421 /// Change the issuer of a collection. Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
422 ///
423 /// # Permissions:
424 /// * Collection issuer
282 ///425 ///
283 /// Parameters:426 /// # Arguments:
284 /// - `origin`: sender of the transaction
285 /// - `collection_id`: collection id of the nft to change issuer of427 /// - `collection_id`: RMRK collection ID to change the issuer of.
286 /// - `new_issuer`: Collection's new issuer428 /// - `new_issuer`: Collection's new issuer.
287 #[transactional]429 #[transactional]
288 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]430 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]
289 pub fn change_collection_issuer(431 pub fn change_collection_issuer(
314 Ok(())456 Ok(())
315 }457 }
316458
317 /// lock collection459 /// "Lock" the collection and prevent new token creation. Cannot be undone.
460 ///
461 /// # Permissions:
462 /// * Collection issuer
463 ///
464 /// # Arguments:
465 /// - `collection_id`: RMRK ID of the collection to lock.
318 #[transactional]466 #[transactional]
319 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]467 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]
320 pub fn lock_collection(468 pub fn lock_collection(
346 Ok(())494 Ok(())
347 }495 }
348496
349 /// Mints an NFT in the specified collection497 /// Mint an NFT in a specified collection.
350 /// Sets metadata and the royalty attribute
351 ///498 ///
352 /// Parameters:499 /// # Permissions:
353 /// - `collection_id`: The class of the asset to be minted.500 /// * Collection issuer
501 ///
502 /// # Arguments:
503 /// - `owner`: Owner account of the NFT. If set to None, defaults to the sender (collection issuer).
354 /// - `nft_id`: The nft value of the asset to be minted.504 /// - `collection_id`: RMRK collection ID for the NFT to be minted within. Cannot be changed.
355 /// - `recipient`: Receiver of the royalty505 /// - `recipient`: Receiver account of the royalty. Has no effect if the `royalty_amount` is not set. Cannot be changed.
356 /// - `royalty`: Permillage reward from each trade for the Recipient506 /// - `royalty_amount`: Optional permillage reward from each trade for the `recipient`. Cannot be changed.
357 /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash507 /// - `metadata`: Arbitrary data about an NFT, e.g. IPFS hash. Cannot be changed.
358 /// - `transferable`: Ability to transfer this NFT508 /// - `transferable`: Can this NFT be transferred? Cannot be changed.
509 /// - `resources`: Resource data to be added to the NFT immediately after minting.
359 #[transactional]510 #[transactional]
360 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]511 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]
361 pub fn mint_nft(512 pub fn mint_nft(
390 &cross_owner,541 &cross_owner,
391 &collection,542 &collection,
392 [543 [
393 Self::rmrk_property(TokenType, &NftType::Regular)?,544 Self::encode_rmrk_property(TokenType, &NftType::Regular)?,
394 Self::rmrk_property(Transferable, &transferable)?,545 Self::encode_rmrk_property(Transferable, &transferable)?,
395 Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,546 Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
396 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,547 Self::encode_rmrk_property(RoyaltyInfo, &royalty_info)?,
397 Self::rmrk_property(Metadata, &metadata)?,548 Self::encode_rmrk_property(Metadata, &metadata)?,
398 Self::rmrk_property(Equipped, &false)?,549 Self::encode_rmrk_property(Equipped, &false)?,
399 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,550 Self::encode_rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
400 Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,551 Self::encode_rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
401 Self::rmrk_property(PendingChildren, &PendingChildrenSet::new())?,552 Self::encode_rmrk_property(PendingChildren, &PendingChildrenSet::new())?,
402 Self::rmrk_property(AssociatedBases, &BasesMap::new())?,553 Self::encode_rmrk_property(AssociatedBases, &BasesMap::new())?,
403 ]554 ]
404 .into_iter(),555 .into_iter(),
405 )556 )
423 Ok(())574 Ok(())
424 }575 }
425576
426 /// burn nft577 /// Burn an NFT, destroying it and its nested tokens up to the specified limit.
578 /// If the burning budget is exceeded, the transaction is reverted.
579 ///
580 /// This is the way to burn a nested token as well.
581 ///
582 /// For more information, see [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively).
583 ///
584 /// # Permissions:
585 /// * Token owner
586 ///
587 /// # Arguments:
588 /// - `collection_id`: RMRK ID of the collection in which the NFT to burn belongs to.
589 /// - `nft_id`: ID of the NFT to be destroyed.
590 /// - `max_burns`: Maximum number of tokens to burn, used for nesting. The transaction
591 /// is reverted if there are more tokens to burn in the nesting tree than this number.
427 #[transactional]592 #[transactional]
428 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]593 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]
429 pub fn burn_nft(594 pub fn burn_nft(
458 Ok(())623 Ok(())
459 }624 }
460625
461 /// Transfers a NFT from an Account or NFT A to another Account or NFT B626 /// Transfer an NFT from an account/NFT A to another account/NFT B.
627 /// The token must be transferable. Nesting cannot occur deeper than the [`NESTING_BUDGET`].
628 ///
629 /// If the target owner is an NFT owned by another account, then the NFT will enter
630 /// the pending state and will have to be accepted by the other account.
462 ///631 ///
463 /// Parameters:632 /// # Permissions:
464 /// - `origin`: sender of the transaction633 /// - Token owner
634 ///
635 /// # Arguments:
465 /// - `rmrk_collection_id`: collection id of the nft to be transferred636 /// - `collection_id`: RMRK ID of the collection of the NFT to be transferred.
466 /// - `rmrk_nft_id`: nft id of the nft to be transferred637 /// - `nft_id`: ID of the NFT to be transferred.
467 /// - `new_owner`: new owner of the nft which can be either an account or a NFT638 /// - `new_owner`: New owner of the nft which can be either an account or a NFT.
468 #[transactional]639 #[transactional]
469 #[pallet::weight(<SelfWeightOf<T>>::send())]640 #[pallet::weight(<SelfWeightOf<T>>::send())]
470 pub fn send(641 pub fn send(
535 <PalletNft<T>>::set_scoped_token_property(706 <PalletNft<T>>::set_scoped_token_property(
536 collection.id,707 collection.id,
537 nft_id,708 nft_id,
538 PropertyScope::Rmrk,709 RMRK_SCOPE,
539 Self::rmrk_property::<Option<PendingTarget>>(710 Self::encode_rmrk_property::<Option<PendingTarget>>(
540 PendingNftAccept,711 PendingNftAccept,
541 &Some((target_collection_id, target_nft_id.into())),712 &Some((target_collection_id, target_nft_id.into())),
542 )?,713 )?,
578 Ok(())749 Ok(())
579 }750 }
580751
581 /// Accepts an NFT sent from another account to self or owned NFT752 /// Accept an NFT sent from another account to self or an owned NFT.
753 ///
754 /// The NFT in question must be pending, and, thus, be [sent](`crate::pallet::Call::send`) first.
755 ///
756 /// # Permissions:
757 /// - Token-owner-to-be
582 ///758 ///
583 /// Parameters:759 /// # Arguments:
584 /// - `origin`: sender of the transaction
585 /// - `rmrk_collection_id`: collection id of the nft to be accepted760 /// - `rmrk_collection_id`: RMRK collection ID of the NFT to be accepted.
586 /// - `rmrk_nft_id`: nft id of the nft to be accepted761 /// - `rmrk_nft_id`: ID of the NFT to be accepted.
587 /// - `new_owner`: either origin's account ID or origin-owned NFT, whichever the NFT was762 /// - `new_owner`: Either the sender's account ID or a sender-owned NFT,
588 /// sent to763 /// whichever the accepted NFT was sent to.
589 #[transactional]764 #[transactional]
590 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]765 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]
591 pub fn accept_nft(766 pub fn accept_nft(
650 <PalletNft<T>>::set_scoped_token_property(825 <PalletNft<T>>::set_scoped_token_property(
651 collection.id,826 collection.id,
652 nft_id,827 nft_id,
653 PropertyScope::Rmrk,828 RMRK_SCOPE,
654 Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,829 Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
655 )?;830 )?;
656 }831 }
657832
665 Ok(())840 Ok(())
666 }841 }
667842
668 /// Rejects an NFT sent from another account to self or owned NFT843 /// Reject an NFT sent from another account to self or owned NFT.
669 ///844 /// The NFT in question will not be sent back and burnt instead.
670 /// Parameters:845 ///
671 /// - `origin`: sender of the transaction846 /// The NFT in question must be pending, and, thus, be [sent](`crate::pallet::Call::send`) first.
847 ///
848 /// # Permissions:
849 /// - Token-owner-to-be-not
850 ///
851 /// # Arguments:
672 /// - `rmrk_collection_id`: collection id of the nft to be accepted852 /// - `rmrk_collection_id`: RMRK ID of the NFT to be rejected.
673 /// - `rmrk_nft_id`: nft id of the nft to be accepted853 /// - `rmrk_nft_id`: ID of the NFT to be rejected.
674 #[transactional]854 #[transactional]
675 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]855 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]
676 pub fn reject_nft(856 pub fn reject_nft(
724 Ok(())904 Ok(())
725 }905 }
726906
727 /// accept the addition of a new resource to an existing NFT907 /// Accept the addition of a newly created pending resource to an existing NFT.
908 ///
909 /// This transaction is needed when a resource is created and assigned to an NFT
910 /// by a non-owner, i.e. the collection issuer, with one of the
911 /// [`add_...` transactions](crate::pallet::Call::add_basic_resource).
912 ///
913 /// # Permissions:
914 /// - Token owner
915 ///
916 /// # Arguments:
917 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
918 /// - `rmrk_nft_id`: ID of the NFT with a pending resource to be accepted.
919 /// - `resource_id`: ID of the newly created pending resource.
728 #[transactional]920 #[transactional]
729 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]921 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]
730 pub fn accept_resource(922 pub fn accept_resource(
767 Ok(())959 Ok(())
768 }960 }
769961
770 /// accept the removal of a resource of an existing NFT962 /// Accept the removal of a removal-pending resource from an NFT.
963 ///
964 /// This transaction is needed when a non-owner, i.e. the collection issuer,
965 /// requests a [removal](`crate::pallet::Call::remove_resource`) of a resource from an NFT.
966 ///
967 /// # Permissions:
968 /// - Token owner
969 ///
970 /// # Arguments:
971 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
972 /// - `rmrk_nft_id`: ID of the NFT with a resource to be removed.
973 /// - `resource_id`: ID of the removal-pending resource.
771 #[transactional]974 #[transactional]
772 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]975 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]
773 pub fn accept_resource_removal(976 pub fn accept_resource_removal(
795998
796 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);999 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);
7971000
798 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1001 let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
7991002
800 let resource_info = <PalletNft<T>>::token_aux_property((1003 let resource_info = <PalletNft<T>>::token_aux_property((
801 collection_id,1004 collection_id,
802 nft_id,1005 nft_id,
803 PropertyScope::Rmrk,1006 RMRK_SCOPE,
804 resource_id_key.clone(),1007 resource_id_key.clone(),
805 ))1008 ))
806 .ok_or(<Error<T>>::ResourceDoesntExist)?;1009 .ok_or(<Error<T>>::ResourceDoesntExist)?;
8071010
808 let resource_info: RmrkResourceInfo = Self::decode_property(&resource_info)?;1011 let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource_info)?;
8091012
810 ensure!(1013 ensure!(
811 resource_info.pending_removal,1014 resource_info.pending_removal,
815 <PalletNft<T>>::remove_token_aux_property(1018 <PalletNft<T>>::remove_token_aux_property(
816 collection_id,1019 collection_id,
817 nft_id,1020 nft_id,
818 PropertyScope::Rmrk,1021 RMRK_SCOPE,
819 resource_id_key,1022 resource_id_key,
820 );1023 );
8211024
833 Ok(())1036 Ok(())
834 }1037 }
8351038
836 /// set a custom value on an NFT1039 /// Add or edit a custom user property, a key-value pair, describing the metadata
1040 /// of a token or a collection, on either one of these.
1041 ///
1042 /// Note that in this proxy implementation many details regarding RMRK are stored
1043 /// as scoped properties prefixed with "rmrk:", normally inaccessible
1044 /// to external transactions and RPCs.
1045 ///
1046 /// # Permissions:
1047 /// - Collection issuer - in case of collection property
1048 /// - Token owner - in case of NFT property
1049 ///
1050 /// # Arguments:
1051 /// - `rmrk_collection_id`: RMRK collection ID.
1052 /// - `maybe_nft_id`: Optional ID of the NFT. If left empty, then the property is set for the collection.
1053 /// - `key`: Key of the custom property to be referenced by.
1054 /// - `value`: Value of the custom property to be stored.
837 #[transactional]1055 #[transactional]
838 #[pallet::weight(<SelfWeightOf<T>>::set_property())]1056 #[pallet::weight(<SelfWeightOf<T>>::set_property())]
839 pub fn set_property(1057 pub fn set_property(
863 <PalletNft<T>>::set_scoped_token_property(1081 <PalletNft<T>>::set_scoped_token_property(
864 collection_id,1082 collection_id,
865 token_id,1083 token_id,
866 PropertyScope::Rmrk,1084 RMRK_SCOPE,
867 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,1085 Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
868 )?;1086 )?;
869 }1087 }
870 None => {1088 None => {
8771095
878 <PalletCommon<T>>::set_scoped_collection_property(1096 <PalletCommon<T>>::set_scoped_collection_property(
879 collection_id,1097 collection_id,
880 PropertyScope::Rmrk,1098 RMRK_SCOPE,
881 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,1099 Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
882 )?;1100 )?;
883 }1101 }
884 }1102 }
893 Ok(())1111 Ok(())
894 }1112 }
8951113
896 /// set a different order of resource priority1114 /// Set a different order of resource priorities for an NFT. Priorities can be used,
1115 /// for example, for order of rendering.
1116 ///
1117 /// Note that the priorities are not updated automatically, and are an empty vector
1118 /// by default. There is no pre-set definition for the order to be particular,
1119 /// it can be interpreted arbitrarily use-case by use-case.
1120 ///
1121 /// # Permissions:
1122 /// - Token owner
1123 ///
1124 /// # Arguments:
1125 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1126 /// - `rmrk_nft_id`: ID of the NFT to rearrange resource priorities for.
1127 /// - `priorities`: Ordered vector of resource IDs.
897 #[transactional]1128 #[transactional]
898 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]1129 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]
899 pub fn set_priority(1130 pub fn set_priority(
920 <PalletNft<T>>::set_scoped_token_property(1151 <PalletNft<T>>::set_scoped_token_property(
921 collection_id,1152 collection_id,
922 nft_id,1153 nft_id,
923 PropertyScope::Rmrk,1154 RMRK_SCOPE,
924 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,1155 Self::encode_rmrk_property(ResourcePriorities, &priorities.into_inner())?,
925 )?;1156 )?;
9261157
927 Self::deposit_event(Event::<T>::PrioritySet {1158 Self::deposit_event(Event::<T>::PrioritySet {
932 Ok(())1163 Ok(())
933 }1164 }
9341165
935 /// Create basic resource1166 /// Create and set/propose a basic resource for an NFT.
1167 ///
1168 /// A resource is considered a part of an NFT, an additional piece of metadata
1169 /// usually serving to add a piece of media on top of the root metadata, be it
1170 /// a different wing on the root template bird or something entirely unrelated.
1171 /// A basic resource is the simplest, lacking a base or composables.
1172 ///
1173 /// See RMRK docs for more information and examples.
1174 ///
1175 /// # Permissions:
1176 /// - Collection issuer - if not the token owner, adding the resource will warrant
1177 /// the owner's [acceptance](crate::pallet::Call::accept_resource).
1178 ///
1179 /// # Arguments:
1180 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1181 /// - `nft_id`: ID of the NFT to assign a resource to.
1182 /// - `resource`: Data of the resource to be created.
936 #[transactional]1183 #[transactional]
937 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]1184 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]
938 pub fn add_basic_resource(1185 pub fn add_basic_resource(
962 Ok(())1209 Ok(())
963 }1210 }
9641211
965 /// Create composable resource1212 /// Create and set/propose a composable resource for an NFT.
1213 ///
1214 /// A resource is considered a part of an NFT, an additional piece of metadata
1215 /// usually serving to add a piece of media on top of the root metadata, be it
1216 /// a different wing on the root template bird or something entirely unrelated.
1217 /// A composable resource links to a base and has a subset of its parts it is composed of.
1218 ///
1219 /// See RMRK docs for more information and examples.
1220 ///
1221 /// # Permissions:
1222 /// - Collection issuer - if not the token owner, adding the resource will warrant
1223 /// the owner's [acceptance](crate::pallet::Call::accept_resource).
1224 ///
1225 /// # Arguments:
1226 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1227 /// - `nft_id`: ID of the NFT to assign a resource to.
1228 /// - `resource`: Data of the resource to be created.
966 #[transactional]1229 #[transactional]
967 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]1230 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]
968 pub fn add_composable_resource(1231 pub fn add_composable_resource(
990 <PalletNft<T>>::try_mutate_token_aux_property(1253 <PalletNft<T>>::try_mutate_token_aux_property(
991 collection_id,1254 collection_id,
992 nft_id.into(),1255 nft_id.into(),
993 PropertyScope::Rmrk,1256 RMRK_SCOPE,
994 Self::rmrk_property_key(AssociatedBases)?,1257 Self::get_scoped_property_key(AssociatedBases)?,
995 |value| -> DispatchResult {1258 |value| -> DispatchResult {
996 let mut bases: BasesMap = match value {1259 let mut bases: BasesMap = match value {
997 Some(value) => Self::decode_property(value)?,1260 Some(value) => Self::decode_property_value(value)?,
998 None => BasesMap::new(),1261 None => BasesMap::new(),
999 };1262 };
10001263
1001 *bases.entry(base_id).or_insert(0) += 1;1264 *bases.entry(base_id).or_insert(0) += 1;
10021265
1003 *value = Some(Self::encode_property(&bases)?);1266 *value = Some(Self::encode_property_value(&bases)?);
1004 Ok(())1267 Ok(())
1005 },1268 },
1006 )?;1269 )?;
1012 Ok(())1275 Ok(())
1013 }1276 }
10141277
1015 /// Create slot resource1278 /// Create and set/propose a slot resource for an NFT.
1279 ///
1280 /// A resource is considered a part of an NFT, an additional piece of metadata
1281 /// usually serving to add a piece of media on top of the root metadata, be it
1282 /// a different wing on the root template bird or something entirely unrelated.
1283 /// A slot resource links to a base and a slot in it which it now occupies.
1284 ///
1285 /// See RMRK docs for more information and examples.
1286 ///
1287 /// # Permissions:
1288 /// - Collection issuer - if not the token owner, adding the resource will warrant
1289 /// the owner's [acceptance](crate::pallet::Call::accept_resource).
1290 ///
1291 /// # Arguments:
1292 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1293 /// - `nft_id`: ID of the NFT to assign a resource to.
1294 /// - `resource`: Data of the resource to be created.
1016 #[transactional]1295 #[transactional]
1017 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]1296 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]
1018 pub fn add_slot_resource(1297 pub fn add_slot_resource(
1042 Ok(())1321 Ok(())
1043 }1322 }
10441323
1045 /// remove resource1324 /// Remove and erase a resource from an NFT.
1325 ///
1326 /// If the sender does not own the NFT, then it will be pending confirmation,
1327 /// and will have to be [accepted](crate::pallet::Call::accept_resource_removal) by the token owner.
1328 ///
1329 /// # Permissions
1330 /// - Collection issuer
1331 ///
1332 /// # Arguments
1333 /// - `collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
1334 /// - `nft_id`: ID of the NFT with a resource to be removed.
1335 /// - `resource_id`: ID of the resource to be removed.
1046 #[transactional]1336 #[transactional]
1047 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]1337 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]
1048 pub fn remove_resource(1338 pub fn remove_resource(
1070}1360}
10711361
1072impl<T: Config> Pallet<T> {1362impl<T: Config> Pallet<T> {
1073 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {1363 /// Transform one of possible RMRK keys into a byte key with a RMRK scope.
1364 pub fn get_scoped_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {
1074 let key = rmrk_key.to_key::<T>()?;1365 let key = rmrk_key.to_key::<T>()?;
10751366
1076 let scoped_key = PropertyScope::Rmrk1367 let scoped_key = RMRK_SCOPE
1077 .apply(key)1368 .apply(key)
1078 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;1369 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;
10791370
1080 Ok(scoped_key)1371 Ok(scoped_key)
1081 }1372 }
10821373
1083 // todo think about renaming these1374 /// Form a Unique property, transforming a RMRK key into bytes (without assigning the scope yet)
1375 /// and encoding the value from an arbitrary type into bytes.
1084 pub fn rmrk_property<E: Encode>(1376 pub fn encode_rmrk_property<E: Encode>(
1085 rmrk_key: RmrkProperty,1377 rmrk_key: RmrkProperty,
1086 value: &E,1378 value: &E,
1087 ) -> Result<Property, DispatchError> {1379 ) -> Result<Property, DispatchError> {
1088 let key = rmrk_key.to_key::<T>()?;1380 let key = rmrk_key.to_key::<T>()?;
10891381
1090 let value = Self::encode_property(value)?;1382 let value = Self::encode_property_value(value)?;
10911383
1092 let property = Property { key, value };1384 let property = Property { key, value };
10931385
1094 Ok(property)1386 Ok(property)
1095 }1387 }
10961388
1097 pub fn encode_property<E: Encode, S: Get<u32>>(1389 /// Encode property value from an arbitrary type into bytes for storage.
1390 pub fn encode_property_value<E: Encode, S: Get<u32>>(
1098 value: &E,1391 value: &E,
1099 ) -> Result<BoundedBytes<S>, DispatchError> {1392 ) -> Result<BoundedBytes<S>, DispatchError> {
1100 let value = value1393 let value = value
1105 Ok(value)1398 Ok(value)
1106 }1399 }
11071400
1108 pub fn decode_property<D: Decode, S: Get<u32>>(1401 /// Decode property value from bytes into an arbitrary type.
1402 pub fn decode_property_value<D: Decode, S: Get<u32>>(
1109 vec: &BoundedBytes<S>,1403 vec: &BoundedBytes<S>,
1110 ) -> Result<D, DispatchError> {1404 ) -> Result<D, DispatchError> {
1111 vec.decode()1405 vec.decode()
1112 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())1406 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())
1113 }1407 }
11141408
1409 /// Change the limit of a property value byte vector.
1115 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1410 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>
1116 where1411 where
1117 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1412 BoundedVec<u8, S>: TryFrom<Vec<u8>>,
1120 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1415 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())
1121 }1416 }
11221417
1418 /// Initialize a new NFT collection with certain RMRK-scoped properties.
1419 ///
1420 /// See [`init_collection`](pallet_nonfungible::pallet::Pallet::init_collection) for more details.
1123 fn init_collection(1421 fn init_collection(
1124 sender: T::CrossAccountId,1422 sender: T::CrossAccountId,
1125 data: CreateCollectionData<T::AccountId>,1423 data: CreateCollectionData<T::AccountId>,
11331431
1134 <PalletCommon<T>>::set_scoped_collection_properties(1432 <PalletCommon<T>>::set_scoped_collection_properties(
1135 collection_id?,1433 collection_id?,
1136 PropertyScope::Rmrk,1434 RMRK_SCOPE,
1137 properties,1435 properties,
1138 )?;1436 )?;
11391437
1140 collection_id1438 collection_id
1141 }1439 }
11421440
1441 /// Mint a new NFT with certain RMRK-scoped properties. Sender must be the collection owner.
1442 ///
1443 /// See [`create_item`](pallet_nonfungible::pallet::Pallet::create_item) for more details.
1143 pub fn create_nft(1444 pub fn create_nft(
1144 sender: &T::CrossAccountId,1445 sender: &T::CrossAccountId,
1145 owner: &T::CrossAccountId,1446 owner: &T::CrossAccountId,
1160 <PalletNft<T>>::set_scoped_token_properties(1461 <PalletNft<T>>::set_scoped_token_properties(
1161 collection.id,1462 collection.id,
1162 nft_id,1463 nft_id,
1163 PropertyScope::Rmrk,1464 RMRK_SCOPE,
1164 properties,1465 properties,
1165 )?;1466 )?;
11661467
1167 Ok(nft_id)1468 Ok(nft_id)
1168 }1469 }
11691470
1471 /// Burn an NFT, along with its nested children, limited by `max_burns`. The sender must be the token owner.
1472 ///
1473 /// See [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively) for more details.
1170 fn destroy_nft(1474 fn destroy_nft(
1171 sender: T::CrossAccountId,1475 sender: T::CrossAccountId,
1172 collection_id: CollectionId,1476 collection_id: CollectionId,
1207 )1511 )
1208 }1512 }
12091513
1514 /// Add a sent token pending acceptance to the target owning token as a property.
1210 fn insert_pending_child(1515 fn insert_pending_child(
1211 target: (CollectionId, TokenId),1516 target: (CollectionId, TokenId),
1212 child: (RmrkCollectionId, RmrkNftId),1517 child: (RmrkCollectionId, RmrkNftId),
1213 ) -> DispatchResult {1518 ) -> DispatchResult {
1214 Self::mutate_pending_child(target, |pending_children| {1519 Self::mutate_pending_children(target, |pending_children| {
1215 pending_children.insert(child);1520 pending_children.insert(child);
1216 })1521 })
1217 }1522 }
12181523
1524 /// Remove a sent token pending acceptance from the target token's properties.
1219 fn remove_pending_child(1525 fn remove_pending_child(
1220 target: (CollectionId, TokenId),1526 target: (CollectionId, TokenId),
1221 child: (RmrkCollectionId, RmrkNftId),1527 child: (RmrkCollectionId, RmrkNftId),
1222 ) -> DispatchResult {1528 ) -> DispatchResult {
1223 Self::mutate_pending_child(target, |pending_children| {1529 Self::mutate_pending_children(target, |pending_children| {
1224 pending_children.remove(&child);1530 pending_children.remove(&child);
1225 })1531 })
1226 }1532 }
12271533
1228 fn mutate_pending_child(1534 /// Apply a mutation to the property of a token containing sent tokens
1535 /// that are currently pending acceptance.
1536 fn mutate_pending_children(
1229 (target_collection_id, target_nft_id): (CollectionId, TokenId),1537 (target_collection_id, target_nft_id): (CollectionId, TokenId),
1230 f: impl FnOnce(&mut PendingChildrenSet),1538 f: impl FnOnce(&mut PendingChildrenSet),
1231 ) -> DispatchResult {1539 ) -> DispatchResult {
1232 <PalletNft<T>>::try_mutate_token_aux_property(1540 <PalletNft<T>>::try_mutate_token_aux_property(
1233 target_collection_id,1541 target_collection_id,
1234 target_nft_id,1542 target_nft_id,
1235 PropertyScope::Rmrk,1543 RMRK_SCOPE,
1236 Self::rmrk_property_key(PendingChildren)?,1544 Self::get_scoped_property_key(PendingChildren)?,
1237 |pending_children| -> DispatchResult {1545 |pending_children| -> DispatchResult {
1238 let mut map = match pending_children {1546 let mut map = match pending_children {
1239 Some(map) => Self::decode_property(map)?,1547 Some(map) => Self::decode_property_value(map)?,
1240 None => PendingChildrenSet::new(),1548 None => PendingChildrenSet::new(),
1241 };1549 };
12421550
1243 f(&mut map);1551 f(&mut map);
12441552
1245 *pending_children = Some(Self::encode_property(&map)?);1553 *pending_children = Some(Self::encode_property_value(&map)?);
12461554
1247 Ok(())1555 Ok(())
1248 },1556 },
1249 )1557 )
1250 }1558 }
12511559
1560 /// Get an iterator from a token's property containing tokens sent to it
1561 /// that are currently pending acceptance.
1252 fn iterate_pending_children(1562 fn iterate_pending_children(
1253 collection_id: CollectionId,1563 collection_id: CollectionId,
1254 nft_id: TokenId,1564 nft_id: TokenId,
1255 ) -> Result<impl Iterator<Item = PendingChild>, DispatchError> {1565 ) -> Result<impl Iterator<Item = PendingChild>, DispatchError> {
1256 let property = <PalletNft<T>>::token_aux_property((1566 let property = <PalletNft<T>>::token_aux_property((
1257 collection_id,1567 collection_id,
1258 nft_id,1568 nft_id,
1259 PropertyScope::Rmrk,1569 RMRK_SCOPE,
1260 Self::rmrk_property_key(PendingChildren)?,1570 Self::get_scoped_property_key(PendingChildren)?,
1261 ));1571 ));
12621572
1263 let pending_children = match property {1573 let pending_children = match property {
1264 Some(map) => Self::decode_property(&map)?,1574 Some(map) => Self::decode_property_value(&map)?,
1265 None => PendingChildrenSet::new(),1575 None => PendingChildrenSet::new(),
1266 };1576 };
12671577
1268 Ok(pending_children.into_iter())1578 Ok(pending_children.into_iter())
1269 }1579 }
12701580
1581 /// Get incremented resource ID from within an NFT's properties and store the new latest ID.
1582 /// Thus, the returned resource ID should be used.
1271 fn acquire_next_resource_id(1583 fn acquire_next_resource_id(
1272 collection_id: CollectionId,1584 collection_id: CollectionId,
1273 nft_id: TokenId,1585 nft_id: TokenId,
1282 <PalletNft<T>>::set_scoped_token_property(1594 <PalletNft<T>>::set_scoped_token_property(
1283 collection_id,1595 collection_id,
1284 nft_id,1596 nft_id,
1285 PropertyScope::Rmrk,1597 RMRK_SCOPE,
1286 Self::rmrk_property(NextResourceId, &next_id)?,1598 Self::encode_rmrk_property(NextResourceId, &next_id)?,
1287 )?;1599 )?;
12881600
1289 Ok(resource_id)1601 Ok(resource_id)
1290 }1602 }
12911603
1604 /// Create and add a resource for a regular NFT, mark it as pending if the sender
1605 /// is not the token owner. The sender must be the collection owner.
1292 fn resource_add(1606 fn resource_add(
1293 sender: T::AccountId,1607 sender: T::AccountId,
1294 collection_id: CollectionId,1608 collection_id: CollectionId,
1319 <PalletNft<T>>::try_mutate_token_aux_property(1633 <PalletNft<T>>::try_mutate_token_aux_property(
1320 collection_id,1634 collection_id,
1321 nft_id,1635 nft_id,
1322 PropertyScope::Rmrk,1636 RMRK_SCOPE,
1323 Self::rmrk_property_key(ResourceId(id))?,1637 Self::get_scoped_property_key(ResourceId(id))?,
1324 |value| -> DispatchResult {1638 |value| -> DispatchResult {
1325 *value = Some(Self::encode_property(&resource_info)?);1639 *value = Some(Self::encode_property_value(&resource_info)?);
13261640
1327 Ok(())1641 Ok(())
1328 },1642 },
1331 Ok(id)1645 Ok(id)
1332 }1646 }
13331647
1648 /// Designate a resource for erasure from an NFT, and remove it if the sender is the token owner.
1649 /// The sender must be the collection owner.
1334 fn resource_remove(1650 fn resource_remove(
1335 sender: T::AccountId,1651 sender: T::AccountId,
1336 collection_id: CollectionId,1652 collection_id: CollectionId,
1341 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1657 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;
1342 ensure!(collection.owner == sender, Error::<T>::NoPermission);1658 ensure!(collection.owner == sender, Error::<T>::NoPermission);
13431659
1344 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1660 let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
1345 let scope = PropertyScope::Rmrk;
13461661
1347 let resource = <PalletNft<T>>::token_aux_property((1662 let resource = <PalletNft<T>>::token_aux_property((
1348 collection_id,1663 collection_id,
1349 nft_id,1664 nft_id,
1350 scope,1665 RMRK_SCOPE,
1351 resource_id_key.clone(),1666 resource_id_key.clone(),
1352 ))1667 ))
1353 .ok_or(<Error<T>>::ResourceDoesntExist)?;1668 .ok_or(<Error<T>>::ResourceDoesntExist)?;
13541669
1355 let resource_info: RmrkResourceInfo = Self::decode_property(&resource)?;1670 let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource)?;
13561671
1357 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1672 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
1358 let topmost_owner =1673 let topmost_owner =
1363 <PalletNft<T>>::remove_token_aux_property(1678 <PalletNft<T>>::remove_token_aux_property(
1364 collection_id,1679 collection_id,
1365 nft_id,1680 nft_id,
1366 PropertyScope::Rmrk,1681 RMRK_SCOPE,
1367 Self::rmrk_property_key(ResourceId(resource_id))?,1682 Self::get_scoped_property_key(ResourceId(resource_id))?,
1368 );1683 );
13691684
1370 if let RmrkResourceTypes::Composable(resource) = resource_info.resource {1685 if let RmrkResourceTypes::Composable(resource) = resource_info.resource {
1383 Ok(())1698 Ok(())
1384 }1699 }
13851700
1701 /// Remove one usage of a base from an NFT's property of associated bases. The base will stay, however,
1702 /// if the count of resources using the base is still non-zero.
1386 fn remove_associated_base_id(1703 fn remove_associated_base_id(
1387 collection_id: CollectionId,1704 collection_id: CollectionId,
1388 nft_id: TokenId,1705 nft_id: TokenId,
1391 <PalletNft<T>>::try_mutate_token_aux_property(1708 <PalletNft<T>>::try_mutate_token_aux_property(
1392 collection_id,1709 collection_id,
1393 nft_id,1710 nft_id,
1394 PropertyScope::Rmrk,1711 RMRK_SCOPE,
1395 Self::rmrk_property_key(AssociatedBases)?,1712 Self::get_scoped_property_key(AssociatedBases)?,
1396 |value| -> DispatchResult {1713 |value| -> DispatchResult {
1397 let mut bases: BasesMap = match value {1714 let mut bases: BasesMap = match value {
1398 Some(value) => Self::decode_property(value)?,1715 Some(value) => Self::decode_property_value(value)?,
1399 None => BasesMap::new(),1716 None => BasesMap::new(),
1400 };1717 };
14011718
1407 }1724 }
1408 }1725 }
14091726
1410 *value = Some(Self::encode_property(&bases)?);1727 *value = Some(Self::encode_property_value(&bases)?);
1411 Ok(())1728 Ok(())
1412 },1729 },
1413 )1730 )
1414 }1731 }
14151732
1733 /// Apply a mutation to a resource stored in the token properties of an NFT.
1416 fn try_mutate_resource_info(1734 fn try_mutate_resource_info(
1417 collection_id: CollectionId,1735 collection_id: CollectionId,
1418 nft_id: TokenId,1736 nft_id: TokenId,
1422 <PalletNft<T>>::try_mutate_token_aux_property(1740 <PalletNft<T>>::try_mutate_token_aux_property(
1423 collection_id,1741 collection_id,
1424 nft_id,1742 nft_id,
1425 PropertyScope::Rmrk,1743 RMRK_SCOPE,
1426 Self::rmrk_property_key(ResourceId(resource_id))?,1744 Self::get_scoped_property_key(ResourceId(resource_id))?,
1427 |value| match value {1745 |value| match value {
1428 Some(value) => {1746 Some(value) => {
1429 let mut resource_info: RmrkResourceInfo = Self::decode_property(value)?;1747 let mut resource_info: RmrkResourceInfo = Self::decode_property_value(value)?;
14301748
1431 f(&mut resource_info)?;1749 f(&mut resource_info)?;
14321750
1433 *value = Self::encode_property(&resource_info)?;1751 *value = Self::encode_property_value(&resource_info)?;
14341752
1435 Ok(())1753 Ok(())
1436 }1754 }
1439 )1757 )
1440 }1758 }
14411759
1760 /// Change the owner of an NFT collection, ensuring that the sender is the current owner.
1442 fn change_collection_owner(1761 fn change_collection_owner(
1443 collection_id: CollectionId,1762 collection_id: CollectionId,
1444 collection_type: misc::CollectionType,1763 collection_type: misc::CollectionType,
1454 collection.save()1773 collection.save()
1455 }1774 }
14561775
1776 /// Ensure that an account is the collection owner/issuer, return an error if not.
1457 pub fn check_collection_owner(1777 pub fn check_collection_owner(
1458 collection: &NonfungibleHandle<T>,1778 collection: &NonfungibleHandle<T>,
1459 account: &T::CrossAccountId,1779 account: &T::CrossAccountId,
1463 .map_err(Self::map_unique_err_to_proxy)1783 .map_err(Self::map_unique_err_to_proxy)
1464 }1784 }
14651785
1786 /// Get the latest yet-unused RMRK collection index from the storage.
1466 pub fn last_collection_idx() -> RmrkCollectionId {1787 pub fn last_collection_idx() -> RmrkCollectionId {
1467 <CollectionIndex<T>>::get()1788 <CollectionIndex<T>>::get()
1468 }1789 }
14691790
1791 /// Get a mapping from a RMRK collection ID to its corresponding Unique collection ID.
1470 pub fn unique_collection_id(1792 pub fn unique_collection_id(
1471 rmrk_collection_id: RmrkCollectionId,1793 rmrk_collection_id: RmrkCollectionId,
1472 ) -> Result<CollectionId, DispatchError> {1794 ) -> Result<CollectionId, DispatchError> {
1473 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1795 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)
1474 .map_err(|_| <Error<T>>::CollectionUnknown.into())1796 .map_err(|_| <Error<T>>::CollectionUnknown.into())
1475 }1797 }
14761798
1799 /// Get a mapping from a Unique collection ID to its RMRK collection ID counterpart, if it exists.
1477 pub fn rmrk_collection_id(1800 pub fn rmrk_collection_id(
1478 unique_collection_id: CollectionId,1801 unique_collection_id: CollectionId,
1479 ) -> Result<RmrkCollectionId, DispatchError> {1802 ) -> Result<RmrkCollectionId, DispatchError> {
1480 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)1803 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)
1481 }1804 }
14821805
1806 /// Fetch a Unique NFT collection.
1483 pub fn get_nft_collection(1807 pub fn get_nft_collection(
1484 collection_id: CollectionId,1808 collection_id: CollectionId,
1485 ) -> Result<NonfungibleHandle<T>, DispatchError> {1809 ) -> Result<NonfungibleHandle<T>, DispatchError> {
1492 }1816 }
1493 }1817 }
14941818
1819 /// Check if an NFT collection with such an ID exists.
1495 pub fn collection_exists(collection_id: CollectionId) -> bool {1820 pub fn collection_exists(collection_id: CollectionId) -> bool {
1496 <CollectionHandle<T>>::try_get(collection_id).is_ok()1821 <CollectionHandle<T>>::try_get(collection_id).is_ok()
1497 }1822 }
14981823
1824 /// Fetch and decode a RMRK-scoped collection property value in bytes.
1499 pub fn get_collection_property(1825 pub fn get_collection_property(
1500 collection_id: CollectionId,1826 collection_id: CollectionId,
1501 key: RmrkProperty,1827 key: RmrkProperty,
1502 ) -> Result<PropertyValue, DispatchError> {1828 ) -> Result<PropertyValue, DispatchError> {
1503 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1829 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)
1504 .get(&Self::rmrk_property_key(key)?)1830 .get(&Self::get_scoped_property_key(key)?)
1505 .ok_or(<Error<T>>::CollectionUnknown)?1831 .ok_or(<Error<T>>::CollectionUnknown)?
1506 .clone();1832 .clone();
15071833
1508 Ok(collection_property)1834 Ok(collection_property)
1509 }1835 }
15101836
1837 /// Fetch a RMRK-scoped collection property and decode it from bytes into an appropriate type.
1511 pub fn get_collection_property_decoded<V: Decode>(1838 pub fn get_collection_property_decoded<V: Decode>(
1512 collection_id: CollectionId,1839 collection_id: CollectionId,
1513 key: RmrkProperty,1840 key: RmrkProperty,
1514 ) -> Result<V, DispatchError> {1841 ) -> Result<V, DispatchError> {
1515 Self::decode_property(&Self::get_collection_property(collection_id, key)?)1842 Self::decode_property_value(&Self::get_collection_property(collection_id, key)?)
1516 }1843 }
15171844
1845 /// Get the type of a collection stored in it as a scoped property.
1846 ///
1847 /// RMRK Core proxy differentiates between regular collections as well as RMRK bases as collections.
1518 pub fn get_collection_type(1848 pub fn get_collection_type(
1519 collection_id: CollectionId,1849 collection_id: CollectionId,
1520 ) -> Result<misc::CollectionType, DispatchError> {1850 ) -> Result<misc::CollectionType, DispatchError> {
1527 })1857 })
1528 }1858 }
15291859
1860 /// Ensure that the type of the collection equals the provided type,
1861 /// otherwise return an error.
1530 pub fn ensure_collection_type(1862 pub fn ensure_collection_type(
1531 collection_id: CollectionId,1863 collection_id: CollectionId,
1532 collection_type: misc::CollectionType,1864 collection_type: misc::CollectionType,
1540 Ok(())1872 Ok(())
1541 }1873 }
15421874
1875 /// Fetch an NFT collection, but make sure it has the appropriate type.
1543 pub fn get_typed_nft_collection(1876 pub fn get_typed_nft_collection(
1544 collection_id: CollectionId,1877 collection_id: CollectionId,
1545 collection_type: misc::CollectionType,1878 collection_type: misc::CollectionType,
1549 Self::get_nft_collection(collection_id)1882 Self::get_nft_collection(collection_id)
1550 }1883 }
15511884
1885 /// Same as [`get_typed_nft_collection`](crate::pallet::Pallet::get_typed_nft_collection),
1886 /// but also return the Unique collection ID.
1552 pub fn get_typed_nft_collection_mapped(1887 pub fn get_typed_nft_collection_mapped(
1553 rmrk_collection_id: RmrkCollectionId,1888 rmrk_collection_id: RmrkCollectionId,
1554 collection_type: misc::CollectionType,1889 collection_type: misc::CollectionType,
1563 Ok((collection, unique_collection_id))1898 Ok((collection, unique_collection_id))
1564 }1899 }
15651900
1901 /// Fetch and decode a RMRK-scoped NFT property value in bytes.
1566 pub fn get_nft_property(1902 pub fn get_nft_property(
1567 collection_id: CollectionId,1903 collection_id: CollectionId,
1568 nft_id: TokenId,1904 nft_id: TokenId,
1569 key: RmrkProperty,1905 key: RmrkProperty,
1570 ) -> Result<PropertyValue, DispatchError> {1906 ) -> Result<PropertyValue, DispatchError> {
1571 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1907 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
1572 .get(&Self::rmrk_property_key(key)?)1908 .get(&Self::get_scoped_property_key(key)?)
1573 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?1909 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?
1574 .clone();1910 .clone();
15751911
1576 Ok(nft_property)1912 Ok(nft_property)
1577 }1913 }
15781914
1915 /// Fetch a RMRK-scoped NFT property and decode it from bytes into an appropriate type.
1579 pub fn get_nft_property_decoded<V: Decode>(1916 pub fn get_nft_property_decoded<V: Decode>(
1580 collection_id: CollectionId,1917 collection_id: CollectionId,
1581 nft_id: TokenId,1918 nft_id: TokenId,
1582 key: RmrkProperty,1919 key: RmrkProperty,
1583 ) -> Result<V, DispatchError> {1920 ) -> Result<V, DispatchError> {
1584 Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)1921 Self::decode_property_value(&Self::get_nft_property(collection_id, nft_id, key)?)
1585 }1922 }
15861923
1924 /// Check that an NFT exists.
1587 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1925 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {
1588 <TokenData<T>>::contains_key((collection_id, nft_id))1926 <TokenData<T>>::contains_key((collection_id, nft_id))
1589 }1927 }
15901928
1929 /// Get the type of an NFT stored in it as a scoped property.
1930 ///
1931 /// RMRK Core proxy differentiates between regular NFTs, and RMRK parts and themes.
1591 pub fn get_nft_type(1932 pub fn get_nft_type(
1592 collection_id: CollectionId,1933 collection_id: CollectionId,
1593 token_id: TokenId,1934 token_id: TokenId,
1596 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1937 .map_err(|_| <Error<T>>::NoAvailableNftId.into())
1597 }1938 }
15981939
1940 /// Ensure that the type of the NFT equals the provided type, otherwise return an error.
1599 pub fn ensure_nft_type(1941 pub fn ensure_nft_type(
1600 collection_id: CollectionId,1942 collection_id: CollectionId,
1601 token_id: TokenId,1943 token_id: TokenId,
1607 Ok(())1949 Ok(())
1608 }1950 }
16091951
1952 /// Ensure that an account is the owner of the token, either directly
1953 /// or at the top of the nesting hierarchy; return an error if it is not.
1610 pub fn ensure_nft_owner(1954 pub fn ensure_nft_owner(
1611 collection_id: CollectionId,1955 collection_id: CollectionId,
1612 token_id: TokenId,1956 token_id: TokenId,
1627 Ok(())1971 Ok(())
1628 }1972 }
16291973
1974 /// Fetch non-scoped properties of a collection or a token that match the filter keys supplied,
1975 /// or, if None are provided, return all non-scoped properties.
1630 pub fn filter_user_properties<Key, Value, R, Mapper>(1976 pub fn filter_user_properties<Key, Value, R, Mapper>(
1631 collection_id: CollectionId,1977 collection_id: CollectionId,
1632 token_id: Option<TokenId>,1978 token_id: Option<TokenId>,
1672 })2018 })
1673 }2019 }
16742020
2021 /// Get all non-scoped properties from a collection or a token, and apply some transformation
2022 /// to each key-value pair.
1675 pub fn iterate_user_properties<Key, Value, R, Mapper>(2023 pub fn iterate_user_properties<Key, Value, R, Mapper>(
1676 collection_id: CollectionId,2024 collection_id: CollectionId,
1677 token_id: Option<TokenId>,2025 token_id: Option<TokenId>,
1699 Ok(properties)2047 Ok(properties)
1700 }2048 }
17012049
2050 /// Match Unique errors to RMRK's own and return the RMRK error if a match is successful.
1702 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {2051 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {
1703 map_unique_err_to_proxy! {2052 map_unique_err_to_proxy! {
1704 match err {2053 match err {
modifiedpallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! Miscellaneous helpers and utilities used by the proxy pallet.
1618
17use super::*;19use super::*;
18use codec::{Encode, Decode, Error};20use codec::{Encode, Decode, Error};
1921
22/// Match errors from one type to another and return an error
23/// if a match is successful.
20#[macro_export]24#[macro_export]
21macro_rules! map_unique_err_to_proxy {25macro_rules! map_unique_err_to_proxy {
22 (match $err:ident { $($unique_err_ty:ident :: $unique_err:ident => $proxy_err:ident),+ $(,)? }) => {26 (match $err:ident { $($unique_err_ty:ident :: $unique_err:ident => $proxy_err:ident),+ $(,)? }) => {
30 };34 };
31}35}
3236
33// Utilize the RmrkCore pallet for access to Runtime errors.37/// Interface to decode bytes from a bounded vector into an arbitrary type.
34pub trait RmrkDecode<T: Decode, S> {38pub trait RmrkDecode<T: Decode, S> {
39 /// Try to decode bytes from a bounded vector into an arbitrary type.
35 fn decode(&self) -> Result<T, Error>;40 fn decode(&self) -> Result<T, Error>;
36}41}
3742
43 }48 }
44}49}
4550
46// Utilize the RmrkCore pallet for access to Runtime errors.51/// Interface to "rebind", change the limit of a bounded byte vector.
47pub trait RmrkRebind<T, S> {52pub trait RmrkRebind<T, S> {
53 /// Try to change the limit of a bounded byte vector.
48 fn rebind(&self) -> Result<BoundedVec<u8, S>, Error>;54 fn rebind(&self) -> Result<BoundedVec<u8, S>, Error>;
49}55}
5056
58 }64 }
59}65}
6066
67/// RMRK Base shares functionality with a regular collection, and is thus
68/// stored as one, but they are used for different purposes and need to be differentiated.
61#[derive(Encode, Decode, PartialEq, Eq)]69#[derive(Encode, Decode, PartialEq, Eq)]
62pub enum CollectionType {70pub enum CollectionType {
63 Regular,71 Regular,
64 Base,72 Base,
65}73}
6674
75/// RMRK Base, being stored as a collection, can have different kinds of tokens,
76/// all except the `Regular` type, which is attributed to `Regular` collection.
67#[derive(Encode, Decode, PartialEq, Eq)]77#[derive(Encode, Decode, PartialEq, Eq)]
68pub enum NftType {78pub enum NftType {
69 Regular,79 Regular,
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! Details of storing and handling RMRK properties.
1618
17use super::*;19use super::*;
18use up_data_structs::PropertyScope;20use up_data_structs::PropertyScope;
19use core::convert::AsRef;21use core::convert::AsRef;
2022
23/// Property prefix for storing resources.
21pub const RESOURCE_ID_PREFIX: &str = "rsid-";24pub const RESOURCE_ID_PREFIX: &str = "rsid-";
25/// Property prefix for storing custom user-defined properties.
22pub const USER_PROPERTY_PREFIX: &str = "userprop-";26pub const USER_PROPERTY_PREFIX: &str = "userprop-";
2327/// Property scope for RMRK, used to signify that this property
28/// was created and is used by RMRK.
29pub const RMRK_SCOPE: PropertyScope = PropertyScope::Rmrk;
30
31/// Predefined RMRK property keys for storage of RMRK data format on the Unique chain.
24pub enum RmrkProperty<'r> {32pub enum RmrkProperty<'r> {
25 Metadata,33 Metadata,
26 CollectionType,34 CollectionType,
49}57}
5058
51impl<'r> RmrkProperty<'r> {59impl<'r> RmrkProperty<'r> {
60 /// Convert a predefined RMRK property key enum into string bytes.
52 pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {61 pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {
53 fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {62 fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {
54 container.as_ref()63 container.as_ref()
94 }103 }
95}104}
96105
106/// Strip a property key of its prefix and RMRK scope.
97pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {107pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {
98 let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;108 let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;
99 let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;109 let key_prefix = RMRK_SCOPE.apply(key_prefix).ok()?;
100110
101 key.as_slice()111 key.as_slice()
102 .strip_prefix(key_prefix.as_slice())?112 .strip_prefix(key_prefix.as_slice())?
105 .ok()115 .ok()
106}116}
107117
118/// Check that the key has the prefix.
108pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {119pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {
109 strip_key_prefix(key, prefix).is_some()120 strip_key_prefix(key, prefix).is_some()
110}121}
modifiedpallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
2// This file is part of Unique Network.
3
4// Unique Network is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Unique Network is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! Realizations of RMRK RPCs (remote procedure calls) related to the Core pallet.
18
1use super::*;19use super::*;
220
21/// Get the latest created collection ID.
3pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {22pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {
4 Ok(<Pallet<T>>::last_collection_idx())23 Ok(<Pallet<T>>::last_collection_idx())
5}24}
625
26/// Get collection info by ID.
7pub fn collection_by_id<T: Config>(27pub fn collection_by_id<T: Config>(
8 collection_id: RmrkCollectionId,28 collection_id: RmrkCollectionId,
9) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {29) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {
29 }))49 }))
30}50}
3151
52/// Get NFT info by collection and NFT IDs.
32pub fn nft_by_id<T: Config>(53pub fn nft_by_id<T: Config>(
33 collection_id: RmrkCollectionId,54 collection_id: RmrkCollectionId,
34 nft_by_id: RmrkNftId,55 nft_by_id: RmrkNftId,
84}105}
85106
107
108/// Get tokens owned by an account in a collection.
86pub fn account_tokens<T: Config>(109pub fn account_tokens<T: Config>(
87 account_id: T::AccountId,110 account_id: T::AccountId,
88 collection_id: RmrkCollectionId,111 collection_id: RmrkCollectionId,
116 Ok(tokens)139 Ok(tokens)
117}140}
118141
142/// Get tokens nested in an NFT - its direct children (not the children's children).
119pub fn nft_children<T: Config>(143pub fn nft_children<T: Config>(
120 collection_id: RmrkCollectionId,144 collection_id: RmrkCollectionId,
121 nft_id: RmrkNftId,145 nft_id: RmrkNftId,
152 )176 )
153}177}
154178
179/// Get collection properties, created by the user - not the proxy-specific properties.
155pub fn collection_properties<T: Config>(180pub fn collection_properties<T: Config>(
156 collection_id: RmrkCollectionId,181 collection_id: RmrkCollectionId,
157 filter_keys: Option<Vec<RmrkPropertyKey>>,182 filter_keys: Option<Vec<RmrkPropertyKey>>,
174 Ok(properties)199 Ok(properties)
175}200}
176201
202/// Get NFT properties, created by the user - not the proxy-specific properties.
177pub fn nft_properties<T: Config>(203pub fn nft_properties<T: Config>(
178 collection_id: RmrkCollectionId,204 collection_id: RmrkCollectionId,
179 nft_id: RmrkNftId,205 nft_id: RmrkNftId,
199 Ok(properties)225 Ok(properties)
200}226}
201227
228/// Get data of resources of an NFT.
202pub fn nft_resources<T: Config>(229pub fn nft_resources<T: Config>(
203 collection_id: RmrkCollectionId,230 collection_id: RmrkCollectionId,
204 nft_id: RmrkNftId,231 nft_id: RmrkNftId,
226 return None;253 return None;
227 }254 }
228255
229 let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;256 let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property_value(&value).ok()?;
230257
231 Some(resource_info)258 Some(resource_info)
232 })259 })
235 Ok(resources)262 Ok(resources)
236}263}
237264
265/// Get the priority of a resource in an NFT.
238pub fn nft_resource_priority<T: Config>(266pub fn nft_resource_priority<T: Config>(
239 collection_id: RmrkCollectionId,267 collection_id: RmrkCollectionId,
240 nft_id: RmrkNftId,268 nft_id: RmrkNftId,
modifiedpallets/proxy-rmrk-equip/src/benchmarking.rsdiffbeforeafterboth
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
2// This file is part of Unique Network.
3
4// Unique Network is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Unique Network is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
1use sp_std::vec;17use sp_std::vec;
218
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
14// You should have received a copy of the GNU General Public License14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! # RMRK Core Proxy Pallet
18//!
19//! A pallet used as proxy for RMRK Core (<https://rmrk-team.github.io/rmrk-substrate/#/pallets/rmrk-core>).
20//!
21//! - [`Config`]
22//! - [`Call`]
23//! - [`Pallet`]
24//!
25//! ## Overview
26//!
27//! The RMRK Equip Proxy pallet mirrors the functionality of RMRK Equip,
28//! binding its externalities to Unique's own underlying structure.
29//! It is purposed to mimic RMRK Equip exactly, allowing seamless integrations
30//! of solutions based on RMRK.
31//!
32//! RMRK Equip itself contains functionality to equip NFTs, and work with Bases,
33//! Parts, and Themes.
34//!
35//! Equip Proxy is responsible for a more specific area of RMRK, and heavily relies on the Core.
36//! For a more foundational description of proxy implementation, please refer to [`pallet_rmrk_core`].
37//!
38//! *Note*, that while RMRK itself is subject to active development and restructuring,
39//! the proxy may be caught temporarily out of date.
40//!
41//! ### What is RMRK?
42//!
43//! RMRK is a set of NFT standards which compose several "NFT 2.0 lego" primitives.
44//! Putting these legos together allows a user to create NFT systems of arbitrary complexity.
45//!
46//! Meaning, RMRK NFTs are dynamic, able to nest into each other and form a hierarchy,
47//! make use of specific changeable and partially shared metadata in the form of resources,
48//! and more.
49//!
50//! Visit RMRK documentation and repositories to learn more:
51//! - Docs: <https://docs.rmrk.app/getting-started/>
52//! - FAQ: <https://coda.io/@rmrk/faq>
53//! - Substrate code repository: <https://github.com/rmrk-team/rmrk-substrate>
54//! - RMRK spec repository: <https://github.com/rmrk-team/rmrk-spec>
55//!
56//! ## Proxy Implementation
57//!
58//! An external user is supposed to be able to utilize this proxy as they would
59//! utilize RMRK, and get exactly the same results. Normally, Unique transactions
60//! are off-limits to RMRK collections and tokens, and vice versa. However,
61//! the information stored on chain can be freely interpreted by storage reads and RPCs.
62//!
63//! ### ID Mapping
64//!
65//! RMRK's collections' IDs are counted independently of Unique's and start at 0.
66//! Note that tokens' IDs still start at 1.
67//! The collections themselves, as well as tokens, are stored as Unique collections,
68//! and thus RMRK IDs are mapped to Unique IDs (but not vice versa).
69//!
70//! ### External/Internal Collection Insulation
71//!
72//! A Unique transaction cannot target collections purposed for RMRK,
73//! and they are flagged as `external` to specify that. On the other hand,
74//! due to the mapping, RMRK transactions and RPCs simply cannot reach Unique collections.
75//!
76//! ### Native Properties
77//!
78//! Many of RMRK's native parameters are stored as scoped properties of a collection
79//! or an NFT on the chain. Scoped properties are prefixed with `rmrk:`, where `:`
80//! is an unacceptable symbol in user-defined proeprties, which, along with other safeguards,
81//! makes them impossible to tamper with.
82//!
83//! ### Collection and NFT Types
84//!
85//! RMRK introduces the concept of a Base, which is a catalgoue of Parts,
86//! possible components of an NFT. Due to its similarity with the functionality
87//! of a token collection, a Base is stored and handled as one, and the Base's Parts and Themes
88//! are the collection's NFTs. See [`CollectionType`](pallet_rmrk_core::misc::CollectionType) and
89//! [`NftType`](pallet_rmrk_core::misc::NftType).
90//!
91//! ## Interface
92//!
93//! ### Dispatchables
94//!
95//! - `create_base` - Create a new Base.
96//! - `theme_add` - Add a Theme to a Base.
97//! - `equippable` - Update the array of Collections allowed to be equipped to a Base's specified Slot Part.
1698
17#![cfg_attr(not(feature = "std"), no_std)]99#![cfg_attr(not(feature = "std"), no_std)]
18100
45127
46 #[pallet::config]128 #[pallet::config]
47 pub trait Config: frame_system::Config + pallet_rmrk_core::Config {129 pub trait Config: frame_system::Config + pallet_rmrk_core::Config {
130 /// Overarching event type.
48 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;131 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
132
133 /// The weight information of this pallet.
49 type WeightInfo: WeightInfo;134 type WeightInfo: WeightInfo;
50 }135 }
51136
137 /// Map of a base ID and a part ID to an NFT in the base collection serving as the part.
52 #[pallet::storage]138 #[pallet::storage]
53 #[pallet::getter(fn internal_part_id)]139 #[pallet::getter(fn internal_part_id)]
54 pub type InernalPartId<T: Config> =140 pub type InernalPartId<T: Config> =
55 StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;141 StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;
56142
143 /// Checkmark that a base has a Theme NFT named "default".
57 #[pallet::storage]144 #[pallet::storage]
58 #[pallet::getter(fn base_has_default_theme)]145 #[pallet::getter(fn base_has_default_theme)]
59 pub type BaseHasDefaultTheme<T: Config> =146 pub type BaseHasDefaultTheme<T: Config> =
78165
79 #[pallet::error]166 #[pallet::error]
80 pub enum Error<T> {167 pub enum Error<T> {
168 /// No permission to perform action.
81 PermissionError,169 PermissionError,
170 /// Could not find an ID for a base collection. It is likely there were too many collections created on the chain.
82 NoAvailableBaseId,171 NoAvailableBaseId,
172 /// Could not find a suitable ID for a part, likely too many part tokens were created in the base.
83 NoAvailablePartId,173 NoAvailablePartId,
174 /// Base collection linked to this ID does not exist.
84 BaseDoesntExist,175 BaseDoesntExist,
176 /// No theme named "default" is associated with the Base.
85 NeedsDefaultThemeFirst,177 NeedsDefaultThemeFirst,
178 /// Part linked to this ID does not exist.
86 PartDoesntExist,179 PartDoesntExist,
180 /// Cannot assign equippables to a fixed part.
87 NoEquippableOnFixedPart,181 NoEquippableOnFixedPart,
88 }182 }
89183
90 #[pallet::call]184 #[pallet::call]
91 impl<T: Config> Pallet<T> {185 impl<T: Config> Pallet<T> {
92 /// Creates a new Base.186 /// Create a new Base.
187 ///
93 /// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)188 /// Modeled after the [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)
189 ///
190 /// # Permissions
191 /// - Anyone - will be assigned as the issuer of the base.
94 ///192 ///
95 /// Parameters:193 /// # Arguments:
96 /// - origin: Caller, will be assigned as the issuer of the Base
97 /// - base_type: media type, e.g. "svg"194 /// - `base_type`: Arbitrary media type, e.g. "svg".
98 /// - symbol: arbitrary client-chosen symbol195 /// - `symbol`: Arbitrary client-chosen symbol.
99 /// - parts: array of Fixed and Slot parts composing the base, confined in length by196 /// - `parts`: Array of Fixed and Slot parts composing the base,
100 /// RmrkPartsLimit197 /// confined in length by [`RmrkPartsLimit`](up_data_structs::RmrkPartsLimit).
101 #[transactional]198 #[transactional]
102 #[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]199 #[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]
103 pub fn create_base(200 pub fn create_base(
131 collection_id,228 collection_id,
132 PropertyScope::Rmrk,229 PropertyScope::Rmrk,
133 [230 [
134 <PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,231 <PalletCore<T>>::encode_rmrk_property(CollectionType, &misc::CollectionType::Base)?,
135 <PalletCore<T>>::rmrk_property(BaseType, &base_type)?,232 <PalletCore<T>>::encode_rmrk_property(BaseType, &base_type)?,
136 ]233 ]
137 .into_iter(),234 .into_iter(),
138 )?;235 )?;
151 Ok(())248 Ok(())
152 }249 }
153250
154 /// Adds a Theme to a Base.251 /// Add a Theme to a Base.
155 /// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)
156 /// Themes are stored in the Themes storage
157 /// A Theme named "default" is required prior to adding other Themes.252 /// A Theme named "default" is required prior to adding other Themes.
253 ///
254 /// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md).
158 ///255 ///
159 /// Parameters:256 /// # Permissions:
160 /// - origin: The caller of the function, must be issuer of the base257 /// - Base issuer
258 ///
259 /// # Arguments:
161 /// - base_id: The Base containing the Theme to be updated260 /// - `base_id`: Base ID containing the Theme to be updated.
162 /// - theme: The Theme to add to the Base. A Theme has a name and properties, which are an261 /// - `theme`: Theme to add to the Base. A Theme has a name and properties, which are an
163 /// array of [key, value, inherit].262 /// array of [key, value, inherit].
164 /// - key: arbitrary BoundedString, defined by client263 /// - `key`: Arbitrary BoundedString, defined by client.
165 /// - value: arbitrary BoundedString, defined by client264 /// - `value`: Arbitrary BoundedString, defined by client.
166 /// - inherit: optional bool265 /// - `inherit`: Optional bool.
167 #[transactional]266 #[transactional]
168 #[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]267 #[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]
169 pub fn theme_add(268 pub fn theme_add(
191 owner,290 owner,
192 &collection,291 &collection,
193 [292 [
194 <PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,293 <PalletCore<T>>::encode_rmrk_property(TokenType, &NftType::Theme)?,
195 <PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,294 <PalletCore<T>>::encode_rmrk_property(ThemeName, &theme.name)?,
196 <PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,295 <PalletCore<T>>::encode_rmrk_property(ThemeInherit, &theme.inherit)?,
197 ]296 ]
198 .into_iter(),297 .into_iter(),
199 )298 )
204 collection_id,303 collection_id,
205 token_id,304 token_id,
206 PropertyScope::Rmrk,305 PropertyScope::Rmrk,
207 <PalletCore<T>>::rmrk_property(306 <PalletCore<T>>::encode_rmrk_property(
208 UserProperty(property.key.as_slice()),307 UserProperty(property.key.as_slice()),
209 &property.value,308 &property.value,
210 )?,309 )?,
214 Ok(())313 Ok(())
215 }314 }
216315
316 /// Update the array of Collections allowed to be equipped to a Base's specified Slot Part.
317 ///
318 /// Modeled after [equippable interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/equippable.md).
319 ///
320 /// # Permissions:
321 /// - Base issuer
322 ///
323 /// # Arguments:
324 /// - `base_id`: Base containing the Slot Part to be updated.
325 /// - `part_id`: Slot Part whose Equippable List is being updated.
326 /// - `equippables`: List of equippables that will override the current Equippables list.
217 #[transactional]327 #[transactional]
218 #[pallet::weight(<SelfWeightOf<T>>::equippable())]328 #[pallet::weight(<SelfWeightOf<T>>::equippable())]
219 pub fn equippable(329 pub fn equippable(
253 base_collection_id,363 base_collection_id,
254 part_id,364 part_id,
255 PropertyScope::Rmrk,365 PropertyScope::Rmrk,
256 <PalletCore<T>>::rmrk_property(EquippableList, &equippables)?,366 <PalletCore<T>>::encode_rmrk_property(EquippableList, &equippables)?,
257 )?;367 )?;
258 }368 }
259 }369 }
266}376}
267377
268impl<T: Config> Pallet<T> {378impl<T: Config> Pallet<T> {
379 /// Create or renew an NFT serving as a part, setting its properties
380 /// to those of the part.
269 fn create_part(381 fn create_part(
270 sender: &T::CrossAccountId,382 sender: &T::CrossAccountId,
271 collection: &NonfungibleHandle<T>,383 collection: &NonfungibleHandle<T>,
298 collection.id,410 collection.id,
299 token_id,411 token_id,
300 PropertyScope::Rmrk,412 PropertyScope::Rmrk,
301 <PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,413 <PalletCore<T>>::encode_rmrk_property(ExternalPartId, &part_id)?,
302 )?;414 )?;
303415
304 token_id416 token_id
310 token_id,422 token_id,
311 PropertyScope::Rmrk,423 PropertyScope::Rmrk,
312 [424 [
313 <PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,425 <PalletCore<T>>::encode_rmrk_property(TokenType, &nft_type)?,
314 <PalletCore<T>>::rmrk_property(Src, &src)?,426 <PalletCore<T>>::encode_rmrk_property(Src, &src)?,
315 <PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,427 <PalletCore<T>>::encode_rmrk_property(ZIndex, &z_index)?,
316 ]428 ]
317 .into_iter(),429 .into_iter(),
318 )?;430 )?;
322 collection.id,434 collection.id,
323 token_id,435 token_id,
324 PropertyScope::Rmrk,436 PropertyScope::Rmrk,
325 <PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,437 <PalletCore<T>>::encode_rmrk_property(EquippableList, &part.equippable)?,
326 )?;438 )?;
327 }439 }
328440
329 Ok(())441 Ok(())
330 }442 }
331443
444 /// Ensure that the collection under the base ID is a base collection,
445 /// and fetch it.
332 fn get_base(base_id: CollectionId) -> Result<NonfungibleHandle<T>, DispatchError> {446 fn get_base(base_id: CollectionId) -> Result<NonfungibleHandle<T>, DispatchError> {
333 let collection =447 let collection =
334 <PalletCore<T>>::get_typed_nft_collection(base_id, misc::CollectionType::Base)448 <PalletCore<T>>::get_typed_nft_collection(base_id, misc::CollectionType::Base)
modifiedpallets/proxy-rmrk-equip/src/rpc.rsdiffbeforeafterboth
1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
2// This file is part of Unique Network.
3
4// Unique Network is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Unique Network is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
16
17//! Realizations of RMRK RPCs (remote procedure calls) related to the Equip pallet.
18
1use super::*;19use super::*;
2use pallet_rmrk_core::{misc, property::*};20use pallet_rmrk_core::{misc, property::*};
3use sp_std::vec::Vec;21use sp_std::vec::Vec;
422
23/// Get base info by its ID.
5pub fn base<T: Config>(24pub fn base<T: Config>(
6 base_id: RmrkBaseId,25 base_id: RmrkBaseId,
7) -> Result<Option<RmrkBaseInfo<T::AccountId>>, DispatchError> {26) -> Result<Option<RmrkBaseInfo<T::AccountId>>, DispatchError> {
22 }))41 }))
23}42}
2443
44/// Get all parts of a base.
25pub fn base_parts<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {45pub fn base_parts<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
26 use pallet_common::CommonCollectionOperations;46 use pallet_common::CommonCollectionOperations;
2747
93 Ok(parts)113 Ok(parts)
94}114}
95115
116/// Get the theme names belonging to a base.
96pub fn theme_names<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {117pub fn theme_names<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
97 use pallet_common::CommonCollectionOperations;118 use pallet_common::CommonCollectionOperations;
98119
124 Ok(theme_names)145 Ok(theme_names)
125}146}
126147
148/// Get theme info, including properties, optionally limited to the provided keys.
127pub fn theme<T: Config>(149pub fn theme<T: Config>(
128 base_id: RmrkBaseId,150 base_id: RmrkBaseId,
129 theme_name: RmrkThemeName,151 theme_name: RmrkThemeName,