git.delta.rocks / unique-network / refs/commits / 281e8172516b

difftreelog

Merge pull request #439 from UniqueNetwork/doc/rmrk

Yaroslav Bolyukin2022-07-22parents: #26e74f7 #4651d7d.patch.diff
in: master

9 files changed

modifiedpallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/benchmarking.rs
+++ b/pallets/proxy-rmrk-core/src/benchmarking.rs
@@ -1,3 +1,19 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
 use sp_std::vec;
 
 use frame_benchmarking::{benchmarks, account};
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 specification repository: <https://github.com/rmrk-team/rmrk-spec>
52//!
53//! ## Terminology
54//!
55//! For more information on RMRK, see RMRK's own documentation.
56//!
57//! ### Intro to RMRK
58//!
59//! - **Resource:** Additional piece of metadata of an NFT usually serving to add
60//! a piece of media on top of the root metadata (NFT's own), be it a different wing
61//! on the root template bird or something entirely unrelated.
62//!
63//! - **Base:** A list of possible "components" - Parts, a combination of which can
64//! be appended/equipped to/on an NFT.
65//!
66//! - **Part:** Something that, together with other Parts, can constitute an NFT.
67//! Parts are defined in the Base to which they belong. Parts can be either
68//! of the `slot` type or `fixed` type. Slots are intended for equippables.
69//! Note that "part of something" and "Part of a Base" can be easily confused,
70//! and so in this documentation these words are distinguished by the capital letter.
71//!
72//! - **Theme:** Named objects of variable => value pairs which get interpolated into
73//! the Base's `themable` Parts. Themes can hold any value, but are often represented
74//! in RMRK's examples as colors applied to visible Parts.
75//!
76//! ### Peculiarities in Unique
77//!
78//! - **Scoped properties:** Properties that are normally obscured from users.
79//! Their purpose is to contain structured metadata that was not included in the Unique standard
80//! for collections and tokens, meant to be operated on by proxies and other outliers.
81//! Scoped property keys are prefixed with `some-scope:`, where `some-scope` is
82//! an arbitrary keyword, like "rmrk". `:` is considered an unacceptable symbol in user-defined
83//! properties, which, along with other safeguards, makes scoped ones impossible to tamper with.
84//!
85//! - **Auxiliary properties:** A slightly different structure of properties,
86//! trading universality of use for more convenient storage, writes and access.
87//! Meant to be inaccessible to end users.
88//!
89//! ## Proxy Implementation
90//!
91//! An external user is supposed to be able to utilize this proxy as they would
92//! utilize RMRK, and get exactly the same results. Normally, Unique transactions
93//! are off-limits to RMRK collections and tokens, and vice versa. However,
94//! the information stored on chain can be freely interpreted by storage reads and Unique RPCs.
95//!
96//! ### ID Mapping
97//!
98//! RMRK's collections' IDs are counted independently of Unique's and start at 0.
99//! Note that tokens' IDs still start at 1.
100//! The collections themselves, as well as tokens, are stored as Unique collections,
101//! and thus RMRK IDs are mapped to Unique IDs (but not vice versa).
102//!
103//! ### External/Internal Collection Insulation
104//!
105//! A Unique transaction cannot target collections purposed for RMRK,
106//! and they are flagged as `external` to specify that. On the other hand,
107//! due to the mapping, RMRK transactions and RPCs simply cannot reach Unique collections.
108//!
109//! ### Native Properties
110//!
111//! Many of RMRK's native parameters are stored as scoped properties of a collection
112//! or an NFT on the chain. Scoped properties are prefixed with `rmrk:`, where `:`
113//! is an unacceptable symbol in user-defined properties, which, along with other safeguards,
114//! makes them impossible to tamper with.
115//!
116//! ### Collection and NFT Types, or Base, Parts and Themes Handling
117//!
118//! RMRK introduces the concept of a Base, which is a catalogue of Parts,
119//! possible components of an NFT. Due to its similarity with the functionality
120//! of a token collection, a Base is stored and handled as one, and the Base's Parts and Themes
121//! are this collection's NFTs. See [`CollectionType`] and [`NftType`].
122//!
123//! ## Interface
124//!
125//! ### Dispatchables
126//!
127//! - `create_collection` - Create a new collection of NFTs.
128//! - `destroy_collection` - Destroy a collection.
129//! - `change_collection_issuer` - Change the issuer of a collection.
130//! Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
131//! - `lock_collection` - "Lock" the collection and prevent new token creation. **Cannot be undone.**
132//! - `mint_nft` - Mint an NFT in a specified collection.
133//! - `burn_nft` - Burn an NFT, destroying it and its nested tokens.
134//! - `send` - Transfer an NFT from an account/NFT A to another account/NFT B.
135//! - `accept_nft` - Accept an NFT sent from another account to self or an owned NFT.
136//! - `reject_nft` - Reject an NFT sent from another account to self or owned NFT and **burn it**.
137//! - `accept_resource` - Accept the addition of a newly created pending resource to an existing NFT.
138//! - `accept_resource_removal` - Accept the removal of a removal-pending resource from an NFT.
139//! - `set_property` - Add or edit a custom user property of a token or a collection.
140//! - `set_priority` - Set a different order of resource priorities for an NFT.
141//! - `add_basic_resource` - Create and set/propose a basic resource for an NFT.
142//! - `add_composable_resource` - Create and set/propose a composable resource for an NFT.
143//! - `add_slot_resource` - Create and set/propose a slot resource for an NFT.
144//! - `remove_resource` - Remove and erase a resource from an NFT.
145
17#![cfg_attr(not(feature = "std"), no_std)]146#![cfg_attr(not(feature = "std"), no_std)]
18147
49178
50use RmrkProperty::*;179use RmrkProperty::*;
51180
181/// Maximum number of levels of depth in the token nesting tree.
52pub const NESTING_BUDGET: u32 = 5;182pub const NESTING_BUDGET: u32 = 5;
53183
54type PendingTarget = (CollectionId, TokenId);184type PendingTarget = (CollectionId, TokenId);
66 pub trait Config:196 pub trait Config:
67 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config197 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config
68 {198 {
199 /// Overarching event type.
69 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;200 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
201
202 /// The weight information of this pallet.
70 type WeightInfo: WeightInfo;203 type WeightInfo: WeightInfo;
71 }204 }
72205
206 /// Latest yet-unused collection ID.
73 #[pallet::storage]207 #[pallet::storage]
74 #[pallet::getter(fn collection_index)]208 #[pallet::getter(fn collection_index)]
75 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;209 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;
76210
211 /// Mapping from RMRK collection ID to Unique's.
77 #[pallet::storage]212 #[pallet::storage]
78 pub type UniqueCollectionId<T: Config> =213 pub type UniqueCollectionId<T: Config> =
79 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;214 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;
159294
160 #[pallet::error]295 #[pallet::error]
161 pub enum Error<T> {296 pub enum Error<T> {
162 /* Unique-specific events */297 /* Unique proxy-specific events */
298 /// Property of the type of RMRK collection could not be read successfully.
163 CorruptedCollectionType,299 CorruptedCollectionType,
164 NftTypeEncodeError,300 // NftTypeEncodeError,
301 /// Too many symbols supplied as the property key. The maximum is [256](up_data_structs::MAX_PROPERTY_KEY_LENGTH).
165 RmrkPropertyKeyIsTooLong,302 RmrkPropertyKeyIsTooLong,
303 /// Too many bytes supplied as the property value. The maximum is [32768](up_data_structs::MAX_PROPERTY_VALUE_LENGTH).
166 RmrkPropertyValueIsTooLong,304 RmrkPropertyValueIsTooLong,
305 /// Could not find a property by the supplied key.
167 RmrkPropertyIsNotFound,306 RmrkPropertyIsNotFound,
307 /// Something went wrong when decoding encoded data from the storage.
308 /// Perhaps, there was a wrong key supplied for the type, or the data was improperly stored.
168 UnableToDecodeRmrkData,309 UnableToDecodeRmrkData,
169310
170 /* RMRK compatible events */311 /* RMRK compatible events */
312 /// Only destroying collections without tokens is allowed.
171 CollectionNotEmpty,313 CollectionNotEmpty,
314 /// Could not find an ID for a collection. It is likely there were too many collections created on the chain, causing an overflow.
172 NoAvailableCollectionId,315 NoAvailableCollectionId,
316 /// Token does not exist, or there is no suitable ID for it, likely too many tokens were created in a collection, causing an overflow.
173 NoAvailableNftId,317 NoAvailableNftId,
318 /// Collection does not exist, has a wrong type, or does not map to a Unique ID.
174 CollectionUnknown,319 CollectionUnknown,
320 /// No permission to perform action.
175 NoPermission,321 NoPermission,
322 /// Token is marked as non-transferable, and thus cannot be transferred.
176 NonTransferable,323 NonTransferable,
324 /// Too many tokens created in the collection, no new ones are allowed.
177 CollectionFullOrLocked,325 CollectionFullOrLocked,
326 /// No such resource found.
178 ResourceDoesntExist,327 ResourceDoesntExist,
328 /// If an NFT is sent to a descendant, that would form a nesting loop, an ouroboros.
329 /// Sending to self is redundant.
179 CannotSendToDescendentOrSelf,330 CannotSendToDescendentOrSelf,
331 /// Not the target owner of the sent NFT.
180 CannotAcceptNonOwnedNft,332 CannotAcceptNonOwnedNft,
333 /// Not the target owner of the sent NFT.
181 CannotRejectNonOwnedNft,334 CannotRejectNonOwnedNft,
335 /// NFT was not sent and is not pending.
182 CannotRejectNonPendingNft,336 CannotRejectNonPendingNft,
337 /// Resource is not pending for the operation.
183 ResourceNotPending,338 ResourceNotPending,
339 /// Could not find an ID for the resource. It is likely there were too many resources created on an NFT, causing an overflow.
184 NoAvailableResourceId,340 NoAvailableResourceId,
185 }341 }
186342
187 #[pallet::call]343 #[pallet::call]
188 impl<T: Config> Pallet<T> {344 impl<T: Config> Pallet<T> {
189 /// Create a collection345 // todo :refactor replace every collection_id with rmrk_collection_id (and nft_id) in arguments for uniformity?
346
347 /// Create a new collection of NFTs.
348 ///
349 /// # Permissions:
350 /// * Anyone - will be assigned as the issuer of the collection.
351 ///
352 /// # Arguments:
353 /// - `metadata`: Metadata describing the collection, e.g. IPFS hash. Cannot be changed.
354 /// - `max`: Optional maximum number of tokens.
355 /// - `symbol`: UTF-8 string with token prefix, by which to represent the token in wallets and UIs.
356 /// Analogous to Unique's [`token_prefix`](up_data_structs::Collection). Cannot be changed.
190 #[transactional]357 #[transactional]
191 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]358 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]
192 pub fn create_collection(359 pub fn create_collection(
226 T::CrossAccountId::from_sub(sender.clone()),393 T::CrossAccountId::from_sub(sender.clone()),
227 data,394 data,
228 [395 [
229 Self::rmrk_property(Metadata, &metadata)?,396 Self::encode_rmrk_property(Metadata, &metadata)?,
230 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,397 Self::encode_rmrk_property(CollectionType, &misc::CollectionType::Regular)?,
231 ]398 ]
232 .into_iter(),399 .into_iter(),
233 )?;400 )?;
237404
238 <PalletCommon<T>>::set_scoped_collection_property(405 <PalletCommon<T>>::set_scoped_collection_property(
239 unique_collection_id,406 unique_collection_id,
240 PropertyScope::Rmrk,407 RMRK_SCOPE,
241 Self::rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,408 Self::encode_rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,
242 )?;409 )?;
243410
244 <CollectionIndex<T>>::mutate(|n| *n += 1);411 <CollectionIndex<T>>::mutate(|n| *n += 1);
251 Ok(())418 Ok(())
252 }419 }
253420
254 /// destroy collection421 /// Destroy a collection.
422 ///
423 /// Only empty collections can be destroyed. If it has any tokens, they must be burned first.
424 ///
425 /// # Permissions:
426 /// * Collection issuer
427 ///
428 /// # Arguments:
429 /// - `collection_id`: RMRK ID of the collection to destroy.
255 #[transactional]430 #[transactional]
256 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]431 #[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]
257 pub fn destroy_collection(432 pub fn destroy_collection(
278 Ok(())453 Ok(())
279 }454 }
280455
281 /// Change the issuer of a collection456 /// Change the issuer of a collection. Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
282 ///457 ///
283 /// Parameters:458 /// # Permissions:
284 /// - `origin`: sender of the transaction459 /// * Collection issuer
460 ///
461 /// # Arguments:
285 /// - `collection_id`: collection id of the nft to change issuer of462 /// - `collection_id`: RMRK collection ID to change the issuer of.
286 /// - `new_issuer`: Collection's new issuer463 /// - `new_issuer`: Collection's new issuer.
287 #[transactional]464 #[transactional]
288 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]465 #[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]
289 pub fn change_collection_issuer(466 pub fn change_collection_issuer(
314 Ok(())491 Ok(())
315 }492 }
316493
317 /// lock collection494 /// "Lock" the collection and prevent new token creation. Cannot be undone.
495 ///
496 /// # Permissions:
497 /// * Collection issuer
498 ///
499 /// # Arguments:
500 /// - `collection_id`: RMRK ID of the collection to lock.
318 #[transactional]501 #[transactional]
319 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]502 #[pallet::weight(<SelfWeightOf<T>>::lock_collection())]
320 pub fn lock_collection(503 pub fn lock_collection(
346 Ok(())529 Ok(())
347 }530 }
348531
349 /// Mints an NFT in the specified collection532 /// Mint an NFT in a specified collection.
350 /// Sets metadata and the royalty attribute
351 ///533 ///
352 /// Parameters:534 /// # Permissions:
353 /// - `collection_id`: The class of the asset to be minted.535 /// * Collection issuer
536 ///
537 /// # Arguments:
538 /// - `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.539 /// - `collection_id`: RMRK collection ID for the NFT to be minted within. Cannot be changed.
355 /// - `recipient`: Receiver of the royalty540 /// - `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 Recipient541 /// - `royalty_amount`: Optional permillage reward from each trade for the `recipient`. Cannot be changed.
357 /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash542 /// - `metadata`: Arbitrary data about an NFT, e.g. IPFS hash. Cannot be changed.
358 /// - `transferable`: Ability to transfer this NFT543 /// - `transferable`: Can this NFT be transferred? Cannot be changed.
544 /// - `resources`: Resource data to be added to the NFT immediately after minting.
359 #[transactional]545 #[transactional]
360 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]546 #[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]
361 pub fn mint_nft(547 pub fn mint_nft(
390 &cross_owner,576 &cross_owner,
391 &collection,577 &collection,
392 [578 [
393 Self::rmrk_property(TokenType, &NftType::Regular)?,579 Self::encode_rmrk_property(TokenType, &NftType::Regular)?,
394 Self::rmrk_property(Transferable, &transferable)?,580 Self::encode_rmrk_property(Transferable, &transferable)?,
395 Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,581 Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
396 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,582 Self::encode_rmrk_property(RoyaltyInfo, &royalty_info)?,
397 Self::rmrk_property(Metadata, &metadata)?,583 Self::encode_rmrk_property(Metadata, &metadata)?,
398 Self::rmrk_property(Equipped, &false)?,584 Self::encode_rmrk_property(Equipped, &false)?,
399 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,585 Self::encode_rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
400 Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,586 Self::encode_rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
401 Self::rmrk_property(PendingChildren, &PendingChildrenSet::new())?,587 Self::encode_rmrk_property(PendingChildren, &PendingChildrenSet::new())?,
402 Self::rmrk_property(AssociatedBases, &BasesMap::new())?,588 Self::encode_rmrk_property(AssociatedBases, &BasesMap::new())?,
403 ]589 ]
404 .into_iter(),590 .into_iter(),
405 )591 )
423 Ok(())609 Ok(())
424 }610 }
425611
426 /// burn nft612 /// Burn an NFT, destroying it and its nested tokens up to the specified limit.
613 /// If the burning budget is exceeded, the transaction is reverted.
614 ///
615 /// This is the way to burn a nested token as well.
616 ///
617 /// For more information, see [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively).
618 ///
619 /// # Permissions:
620 /// * Token owner
621 ///
622 /// # Arguments:
623 /// - `collection_id`: RMRK ID of the collection in which the NFT to burn belongs to.
624 /// - `nft_id`: ID of the NFT to be destroyed.
625 /// - `max_burns`: Maximum number of tokens to burn, assuming nesting. The transaction
626 /// is reverted if there are more tokens to burn in the nesting tree than this number.
627 /// This is primarily a mechanism of transaction weight control.
427 #[transactional]628 #[transactional]
428 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]629 #[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]
429 pub fn burn_nft(630 pub fn burn_nft(
458 Ok(())659 Ok(())
459 }660 }
460661
461 /// Transfers a NFT from an Account or NFT A to another Account or NFT B662 /// Transfer an NFT from an account/NFT A to another account/NFT B.
663 /// The token must be transferable. Nesting cannot occur deeper than the [`NESTING_BUDGET`].
462 ///664 ///
463 /// Parameters:665 /// If the target owner is an NFT owned by another account, then the NFT will enter
464 /// - `origin`: sender of the transaction666 /// the pending state and will have to be accepted by the other account.
465 /// - `rmrk_collection_id`: collection id of the nft to be transferred667 ///
668 /// # Permissions:
669 /// - Token owner
670 ///
671 /// # Arguments:
672 /// - `collection_id`: RMRK ID of the collection of the NFT to be transferred.
466 /// - `rmrk_nft_id`: nft id of the nft to be transferred673 /// - `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 NFT674 /// - `new_owner`: New owner of the nft which can be either an account or a NFT.
468 #[transactional]675 #[transactional]
469 #[pallet::weight(<SelfWeightOf<T>>::send())]676 #[pallet::weight(<SelfWeightOf<T>>::send())]
470 pub fn send(677 pub fn send(
535 <PalletNft<T>>::set_scoped_token_property(742 <PalletNft<T>>::set_scoped_token_property(
536 collection.id,743 collection.id,
537 nft_id,744 nft_id,
538 PropertyScope::Rmrk,745 RMRK_SCOPE,
539 Self::rmrk_property::<Option<PendingTarget>>(746 Self::encode_rmrk_property::<Option<PendingTarget>>(
540 PendingNftAccept,747 PendingNftAccept,
541 &Some((target_collection_id, target_nft_id.into())),748 &Some((target_collection_id, target_nft_id.into())),
542 )?,749 )?,
578 Ok(())785 Ok(())
579 }786 }
580787
581 /// Accepts an NFT sent from another account to self or owned NFT788 /// Accept an NFT sent from another account to self or an owned NFT.
582 ///789 ///
583 /// Parameters:790 /// The NFT in question must be pending, and, thus, be [sent](`Pallet::send`) first.
584 /// - `origin`: sender of the transaction791 ///
792 /// # Permissions:
793 /// - Token-owner-to-be
794 ///
795 /// # Arguments:
585 /// - `rmrk_collection_id`: collection id of the nft to be accepted796 /// - `rmrk_collection_id`: RMRK collection ID of the NFT to be accepted.
586 /// - `rmrk_nft_id`: nft id of the nft to be accepted797 /// - `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 was798 /// - `new_owner`: Either the sender's account ID or a sender-owned NFT,
588 /// sent to799 /// whichever the accepted NFT was sent to.
589 #[transactional]800 #[transactional]
590 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]801 #[pallet::weight(<SelfWeightOf<T>>::accept_nft())]
591 pub fn accept_nft(802 pub fn accept_nft(
650 <PalletNft<T>>::set_scoped_token_property(861 <PalletNft<T>>::set_scoped_token_property(
651 collection.id,862 collection.id,
652 nft_id,863 nft_id,
653 PropertyScope::Rmrk,864 RMRK_SCOPE,
654 Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,865 Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
655 )?;866 )?;
656 }867 }
657868
665 Ok(())876 Ok(())
666 }877 }
667878
668 /// Rejects an NFT sent from another account to self or owned NFT879 /// Reject an NFT sent from another account to self or owned NFT.
880 /// The NFT in question will not be sent back and burnt instead.
669 ///881 ///
670 /// Parameters:882 /// The NFT in question must be pending, and, thus, be [sent](`Pallet::send`) first.
671 /// - `origin`: sender of the transaction883 ///
884 /// # Permissions:
885 /// - Token-owner-to-be-not
886 ///
887 /// # Arguments:
672 /// - `rmrk_collection_id`: collection id of the nft to be accepted888 /// - `rmrk_collection_id`: RMRK ID of the NFT to be rejected.
673 /// - `rmrk_nft_id`: nft id of the nft to be accepted889 /// - `rmrk_nft_id`: ID of the NFT to be rejected.
674 #[transactional]890 #[transactional]
675 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]891 #[pallet::weight(<SelfWeightOf<T>>::reject_nft())]
676 pub fn reject_nft(892 pub fn reject_nft(
724 Ok(())940 Ok(())
725 }941 }
726942
727 /// accept the addition of a new resource to an existing NFT943 /// Accept the addition of a newly created pending resource to an existing NFT.
944 ///
945 /// This transaction is needed when a resource is created and assigned to an NFT
946 /// by a non-owner, i.e. the collection issuer, with one of the
947 /// [`add_...` transactions](Pallet::add_basic_resource).
948 ///
949 /// # Permissions:
950 /// - Token owner
951 ///
952 /// # Arguments:
953 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
954 /// - `rmrk_nft_id`: ID of the NFT with a pending resource to be accepted.
955 /// - `resource_id`: ID of the newly created pending resource.
728 #[transactional]956 #[transactional]
729 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]957 #[pallet::weight(<SelfWeightOf<T>>::accept_resource())]
730 pub fn accept_resource(958 pub fn accept_resource(
767 Ok(())995 Ok(())
768 }996 }
769997
770 /// accept the removal of a resource of an existing NFT998 /// Accept the removal of a removal-pending resource from an NFT.
999 ///
1000 /// This transaction is needed when a non-owner, i.e. the collection issuer,
1001 /// requests a [removal](`Pallet::remove_resource`) of a resource from an NFT.
1002 ///
1003 /// # Permissions:
1004 /// - Token owner
1005 ///
1006 /// # Arguments:
1007 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1008 /// - `rmrk_nft_id`: ID of the NFT with a resource to be removed.
1009 /// - `resource_id`: ID of the removal-pending resource.
771 #[transactional]1010 #[transactional]
772 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]1011 #[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]
773 pub fn accept_resource_removal(1012 pub fn accept_resource_removal(
7951034
796 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);1035 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);
7971036
798 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1037 let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
7991038
800 let resource_info = <PalletNft<T>>::token_aux_property((1039 let resource_info = <PalletNft<T>>::token_aux_property((
801 collection_id,1040 collection_id,
802 nft_id,1041 nft_id,
803 PropertyScope::Rmrk,1042 RMRK_SCOPE,
804 resource_id_key.clone(),1043 resource_id_key.clone(),
805 ))1044 ))
806 .ok_or(<Error<T>>::ResourceDoesntExist)?;1045 .ok_or(<Error<T>>::ResourceDoesntExist)?;
8071046
808 let resource_info: RmrkResourceInfo = Self::decode_property(&resource_info)?;1047 let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource_info)?;
8091048
810 ensure!(1049 ensure!(
811 resource_info.pending_removal,1050 resource_info.pending_removal,
815 <PalletNft<T>>::remove_token_aux_property(1054 <PalletNft<T>>::remove_token_aux_property(
816 collection_id,1055 collection_id,
817 nft_id,1056 nft_id,
818 PropertyScope::Rmrk,1057 RMRK_SCOPE,
819 resource_id_key,1058 resource_id_key,
820 );1059 );
8211060
833 Ok(())1072 Ok(())
834 }1073 }
8351074
836 /// set a custom value on an NFT1075 /// Add or edit a custom user property, a key-value pair, describing the metadata
1076 /// of a token or a collection, on either one of these.
1077 ///
1078 /// Note that in this proxy implementation many details regarding RMRK are stored
1079 /// as scoped properties prefixed with "rmrk:", normally inaccessible
1080 /// to external transactions and RPCs.
1081 ///
1082 /// # Permissions:
1083 /// - Collection issuer - in case of collection property
1084 /// - Token owner - in case of NFT property
1085 ///
1086 /// # Arguments:
1087 /// - `rmrk_collection_id`: RMRK collection ID.
1088 /// - `maybe_nft_id`: Optional ID of the NFT. If left empty, then the property is set for the collection.
1089 /// - `key`: Key of the custom property to be referenced by.
1090 /// - `value`: Value of the custom property to be stored.
837 #[transactional]1091 #[transactional]
838 #[pallet::weight(<SelfWeightOf<T>>::set_property())]1092 #[pallet::weight(<SelfWeightOf<T>>::set_property())]
839 pub fn set_property(1093 pub fn set_property(
863 <PalletNft<T>>::set_scoped_token_property(1117 <PalletNft<T>>::set_scoped_token_property(
864 collection_id,1118 collection_id,
865 token_id,1119 token_id,
866 PropertyScope::Rmrk,1120 RMRK_SCOPE,
867 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,1121 Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
868 )?;1122 )?;
869 }1123 }
870 None => {1124 None => {
8771131
878 <PalletCommon<T>>::set_scoped_collection_property(1132 <PalletCommon<T>>::set_scoped_collection_property(
879 collection_id,1133 collection_id,
880 PropertyScope::Rmrk,1134 RMRK_SCOPE,
881 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,1135 Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
882 )?;1136 )?;
883 }1137 }
884 }1138 }
893 Ok(())1147 Ok(())
894 }1148 }
8951149
896 /// set a different order of resource priority1150 /// Set a different order of resource priorities for an NFT. Priorities can be used,
1151 /// for example, for order of rendering.
1152 ///
1153 /// Note that the priorities are not updated automatically, and are an empty vector
1154 /// by default. There is no pre-set definition for the order to be particular,
1155 /// it can be interpreted arbitrarily use-case by use-case.
1156 ///
1157 /// # Permissions:
1158 /// - Token owner
1159 ///
1160 /// # Arguments:
1161 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1162 /// - `rmrk_nft_id`: ID of the NFT to rearrange resource priorities for.
1163 /// - `priorities`: Ordered vector of resource IDs.
897 #[transactional]1164 #[transactional]
898 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]1165 #[pallet::weight(<SelfWeightOf<T>>::set_priority())]
899 pub fn set_priority(1166 pub fn set_priority(
920 <PalletNft<T>>::set_scoped_token_property(1187 <PalletNft<T>>::set_scoped_token_property(
921 collection_id,1188 collection_id,
922 nft_id,1189 nft_id,
923 PropertyScope::Rmrk,1190 RMRK_SCOPE,
924 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,1191 Self::encode_rmrk_property(ResourcePriorities, &priorities.into_inner())?,
925 )?;1192 )?;
9261193
927 Self::deposit_event(Event::<T>::PrioritySet {1194 Self::deposit_event(Event::<T>::PrioritySet {
932 Ok(())1199 Ok(())
933 }1200 }
9341201
935 /// Create basic resource1202 /// Create and set/propose a basic resource for an NFT.
1203 ///
1204 /// A basic resource is the simplest, lacking a Base and anything that comes with it.
1205 /// See RMRK docs for more information and examples.
1206 ///
1207 /// # Permissions:
1208 /// - Collection issuer - if not the token owner, adding the resource will warrant
1209 /// the owner's [acceptance](Pallet::accept_resource).
1210 ///
1211 /// # Arguments:
1212 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1213 /// - `nft_id`: ID of the NFT to assign a resource to.
1214 /// - `resource`: Data of the resource to be created.
936 #[transactional]1215 #[transactional]
937 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]1216 #[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]
938 pub fn add_basic_resource(1217 pub fn add_basic_resource(
962 Ok(())1241 Ok(())
963 }1242 }
9641243
965 /// Create composable resource1244 /// Create and set/propose a composable resource for an NFT.
1245 ///
1246 /// A composable resource links to a Base and has a subset of its Parts it is composed of.
1247 /// See RMRK docs for more information and examples.
1248 ///
1249 /// # Permissions:
1250 /// - Collection issuer - if not the token owner, adding the resource will warrant
1251 /// the owner's [acceptance](Pallet::accept_resource).
1252 ///
1253 /// # Arguments:
1254 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1255 /// - `nft_id`: ID of the NFT to assign a resource to.
1256 /// - `resource`: Data of the resource to be created.
966 #[transactional]1257 #[transactional]
967 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]1258 #[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]
968 pub fn add_composable_resource(1259 pub fn add_composable_resource(
990 <PalletNft<T>>::try_mutate_token_aux_property(1281 <PalletNft<T>>::try_mutate_token_aux_property(
991 collection_id,1282 collection_id,
992 nft_id.into(),1283 nft_id.into(),
993 PropertyScope::Rmrk,1284 RMRK_SCOPE,
994 Self::rmrk_property_key(AssociatedBases)?,1285 Self::get_scoped_property_key(AssociatedBases)?,
995 |value| -> DispatchResult {1286 |value| -> DispatchResult {
996 let mut bases: BasesMap = match value {1287 let mut bases: BasesMap = match value {
997 Some(value) => Self::decode_property(value)?,1288 Some(value) => Self::decode_property_value(value)?,
998 None => BasesMap::new(),1289 None => BasesMap::new(),
999 };1290 };
10001291
1001 *bases.entry(base_id).or_insert(0) += 1;1292 *bases.entry(base_id).or_insert(0) += 1;
10021293
1003 *value = Some(Self::encode_property(&bases)?);1294 *value = Some(Self::encode_property_value(&bases)?);
1004 Ok(())1295 Ok(())
1005 },1296 },
1006 )?;1297 )?;
1012 Ok(())1303 Ok(())
1013 }1304 }
10141305
1015 /// Create slot resource1306 /// Create and set/propose a slot resource for an NFT.
1307 ///
1308 /// A slot resource links to a Base and a slot ID in it which it can fit into.
1309 /// See RMRK docs for more information and examples.
1310 ///
1311 /// # Permissions:
1312 /// - Collection issuer - if not the token owner, adding the resource will warrant
1313 /// the owner's [acceptance](Pallet::accept_resource).
1314 ///
1315 /// # Arguments:
1316 /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
1317 /// - `nft_id`: ID of the NFT to assign a resource to.
1318 /// - `resource`: Data of the resource to be created.
1016 #[transactional]1319 #[transactional]
1017 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]1320 #[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]
1018 pub fn add_slot_resource(1321 pub fn add_slot_resource(
1042 Ok(())1345 Ok(())
1043 }1346 }
10441347
1045 /// remove resource1348 /// Remove and erase a resource from an NFT.
1349 ///
1350 /// If the sender does not own the NFT, then it will be pending confirmation,
1351 /// and will have to be [accepted](Pallet::accept_resource_removal) by the token owner.
1352 ///
1353 /// # Permissions
1354 /// - Collection issuer
1355 ///
1356 /// # Arguments
1357 /// - `collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
1358 /// - `nft_id`: ID of the NFT with a resource to be removed.
1359 /// - `resource_id`: ID of the resource to be removed.
1046 #[transactional]1360 #[transactional]
1047 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]1361 #[pallet::weight(<SelfWeightOf<T>>::remove_resource())]
1048 pub fn remove_resource(1362 pub fn remove_resource(
1070}1384}
10711385
1072impl<T: Config> Pallet<T> {1386impl<T: Config> Pallet<T> {
1073 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {1387 /// Transform one of possible RMRK keys into a byte key with a RMRK scope.
1388 pub fn get_scoped_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {
1074 let key = rmrk_key.to_key::<T>()?;1389 let key = rmrk_key.to_key::<T>()?;
10751390
1076 let scoped_key = PropertyScope::Rmrk1391 let scoped_key = RMRK_SCOPE
1077 .apply(key)1392 .apply(key)
1078 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;1393 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;
10791394
1080 Ok(scoped_key)1395 Ok(scoped_key)
1081 }1396 }
10821397
1083 // todo think about renaming these1398 /// Form a Unique property, transforming a RMRK key into bytes (without assigning the scope yet)
1399 /// and encoding the value from an arbitrary type into bytes.
1084 pub fn rmrk_property<E: Encode>(1400 pub fn encode_rmrk_property<E: Encode>(
1085 rmrk_key: RmrkProperty,1401 rmrk_key: RmrkProperty,
1086 value: &E,1402 value: &E,
1087 ) -> Result<Property, DispatchError> {1403 ) -> Result<Property, DispatchError> {
1088 let key = rmrk_key.to_key::<T>()?;1404 let key = rmrk_key.to_key::<T>()?;
10891405
1090 let value = Self::encode_property(value)?;1406 let value = Self::encode_property_value(value)?;
10911407
1092 let property = Property { key, value };1408 let property = Property { key, value };
10931409
1094 Ok(property)1410 Ok(property)
1095 }1411 }
10961412
1097 pub fn encode_property<E: Encode, S: Get<u32>>(1413 /// Encode property value from an arbitrary type into bytes for storage.
1414 pub fn encode_property_value<E: Encode, S: Get<u32>>(
1098 value: &E,1415 value: &E,
1099 ) -> Result<BoundedBytes<S>, DispatchError> {1416 ) -> Result<BoundedBytes<S>, DispatchError> {
1100 let value = value1417 let value = value
1105 Ok(value)1422 Ok(value)
1106 }1423 }
11071424
1108 pub fn decode_property<D: Decode, S: Get<u32>>(1425 /// Decode property value from bytes into an arbitrary type.
1426 pub fn decode_property_value<D: Decode, S: Get<u32>>(
1109 vec: &BoundedBytes<S>,1427 vec: &BoundedBytes<S>,
1110 ) -> Result<D, DispatchError> {1428 ) -> Result<D, DispatchError> {
1111 vec.decode()1429 vec.decode()
1112 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())1430 .map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())
1113 }1431 }
11141432
1433 /// Change the limit of a property value byte vector.
1115 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1434 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>
1116 where1435 where
1117 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1436 BoundedVec<u8, S>: TryFrom<Vec<u8>>,
1120 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1439 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())
1121 }1440 }
11221441
1442 /// Initialize a new NFT collection with certain RMRK-scoped properties.
1443 ///
1444 /// See [`init_collection`](pallet_nonfungible::pallet::Pallet::init_collection) for more details.
1123 fn init_collection(1445 fn init_collection(
1124 sender: T::CrossAccountId,1446 sender: T::CrossAccountId,
1125 data: CreateCollectionData<T::AccountId>,1447 data: CreateCollectionData<T::AccountId>,
11331455
1134 <PalletCommon<T>>::set_scoped_collection_properties(1456 <PalletCommon<T>>::set_scoped_collection_properties(
1135 collection_id?,1457 collection_id?,
1136 PropertyScope::Rmrk,1458 RMRK_SCOPE,
1137 properties,1459 properties,
1138 )?;1460 )?;
11391461
1140 collection_id1462 collection_id
1141 }1463 }
11421464
1465 /// Mint a new NFT with certain RMRK-scoped properties. Sender must be the collection owner.
1466 ///
1467 /// See [`create_item`](pallet_nonfungible::pallet::Pallet::create_item) for more details.
1143 pub fn create_nft(1468 pub fn create_nft(
1144 sender: &T::CrossAccountId,1469 sender: &T::CrossAccountId,
1145 owner: &T::CrossAccountId,1470 owner: &T::CrossAccountId,
11571482
1158 let nft_id = <PalletNft<T>>::current_token_id(collection.id);1483 let nft_id = <PalletNft<T>>::current_token_id(collection.id);
11591484
1160 <PalletNft<T>>::set_scoped_token_properties(1485 <PalletNft<T>>::set_scoped_token_properties(collection.id, nft_id, RMRK_SCOPE, properties)?;
1161 collection.id,
1162 nft_id,
1163 PropertyScope::Rmrk,
1164 properties,
1165 )?;
11661486
1167 Ok(nft_id)1487 Ok(nft_id)
1168 }1488 }
11691489
1490 /// Burn an NFT, along with its nested children, limited by `max_burns`. The sender must be the token owner.
1491 ///
1492 /// See [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively) for more details.
1170 fn destroy_nft(1493 fn destroy_nft(
1171 sender: T::CrossAccountId,1494 sender: T::CrossAccountId,
1172 collection_id: CollectionId,1495 collection_id: CollectionId,
1207 )1530 )
1208 }1531 }
12091532
1533 /// Add a sent token pending acceptance to the target owning token as a property.
1210 fn insert_pending_child(1534 fn insert_pending_child(
1211 target: (CollectionId, TokenId),1535 target: (CollectionId, TokenId),
1212 child: (RmrkCollectionId, RmrkNftId),1536 child: (RmrkCollectionId, RmrkNftId),
1213 ) -> DispatchResult {1537 ) -> DispatchResult {
1214 Self::mutate_pending_child(target, |pending_children| {1538 Self::mutate_pending_children(target, |pending_children| {
1215 pending_children.insert(child);1539 pending_children.insert(child);
1216 })1540 })
1217 }1541 }
12181542
1543 /// Remove a sent token pending acceptance from the target token's properties.
1219 fn remove_pending_child(1544 fn remove_pending_child(
1220 target: (CollectionId, TokenId),1545 target: (CollectionId, TokenId),
1221 child: (RmrkCollectionId, RmrkNftId),1546 child: (RmrkCollectionId, RmrkNftId),
1222 ) -> DispatchResult {1547 ) -> DispatchResult {
1223 Self::mutate_pending_child(target, |pending_children| {1548 Self::mutate_pending_children(target, |pending_children| {
1224 pending_children.remove(&child);1549 pending_children.remove(&child);
1225 })1550 })
1226 }1551 }
12271552
1228 fn mutate_pending_child(1553 /// Apply a mutation to the property of a token containing sent tokens
1554 /// that are currently pending acceptance.
1555 fn mutate_pending_children(
1229 (target_collection_id, target_nft_id): (CollectionId, TokenId),1556 (target_collection_id, target_nft_id): (CollectionId, TokenId),
1230 f: impl FnOnce(&mut PendingChildrenSet),1557 f: impl FnOnce(&mut PendingChildrenSet),
1231 ) -> DispatchResult {1558 ) -> DispatchResult {
1232 <PalletNft<T>>::try_mutate_token_aux_property(1559 <PalletNft<T>>::try_mutate_token_aux_property(
1233 target_collection_id,1560 target_collection_id,
1234 target_nft_id,1561 target_nft_id,
1235 PropertyScope::Rmrk,1562 RMRK_SCOPE,
1236 Self::rmrk_property_key(PendingChildren)?,1563 Self::get_scoped_property_key(PendingChildren)?,
1237 |pending_children| -> DispatchResult {1564 |pending_children| -> DispatchResult {
1238 let mut map = match pending_children {1565 let mut map = match pending_children {
1239 Some(map) => Self::decode_property(map)?,1566 Some(map) => Self::decode_property_value(map)?,
1240 None => PendingChildrenSet::new(),1567 None => PendingChildrenSet::new(),
1241 };1568 };
12421569
1243 f(&mut map);1570 f(&mut map);
12441571
1245 *pending_children = Some(Self::encode_property(&map)?);1572 *pending_children = Some(Self::encode_property_value(&map)?);
12461573
1247 Ok(())1574 Ok(())
1248 },1575 },
1249 )1576 )
1250 }1577 }
12511578
1579 /// Get an iterator from a token's property containing tokens sent to it
1580 /// that are currently pending acceptance.
1252 fn iterate_pending_children(1581 fn iterate_pending_children(
1253 collection_id: CollectionId,1582 collection_id: CollectionId,
1254 nft_id: TokenId,1583 nft_id: TokenId,
1255 ) -> Result<impl Iterator<Item = PendingChild>, DispatchError> {1584 ) -> Result<impl Iterator<Item = PendingChild>, DispatchError> {
1256 let property = <PalletNft<T>>::token_aux_property((1585 let property = <PalletNft<T>>::token_aux_property((
1257 collection_id,1586 collection_id,
1258 nft_id,1587 nft_id,
1259 PropertyScope::Rmrk,1588 RMRK_SCOPE,
1260 Self::rmrk_property_key(PendingChildren)?,1589 Self::get_scoped_property_key(PendingChildren)?,
1261 ));1590 ));
12621591
1263 let pending_children = match property {1592 let pending_children = match property {
1264 Some(map) => Self::decode_property(&map)?,1593 Some(map) => Self::decode_property_value(&map)?,
1265 None => PendingChildrenSet::new(),1594 None => PendingChildrenSet::new(),
1266 };1595 };
12671596
1268 Ok(pending_children.into_iter())1597 Ok(pending_children.into_iter())
1269 }1598 }
12701599
1600 /// Get incremented resource ID from within an NFT's properties and store the new latest ID.
1601 /// Thus, the returned resource ID should be used.
1602 ///
1603 /// Resource IDs are unique only across an NFT.
1271 fn acquire_next_resource_id(1604 fn acquire_next_resource_id(
1272 collection_id: CollectionId,1605 collection_id: CollectionId,
1273 nft_id: TokenId,1606 nft_id: TokenId,
1282 <PalletNft<T>>::set_scoped_token_property(1615 <PalletNft<T>>::set_scoped_token_property(
1283 collection_id,1616 collection_id,
1284 nft_id,1617 nft_id,
1285 PropertyScope::Rmrk,1618 RMRK_SCOPE,
1286 Self::rmrk_property(NextResourceId, &next_id)?,1619 Self::encode_rmrk_property(NextResourceId, &next_id)?,
1287 )?;1620 )?;
12881621
1289 Ok(resource_id)1622 Ok(resource_id)
1290 }1623 }
12911624
1625 /// Create and add a resource for a regular NFT, mark it as pending if the sender
1626 /// is not the token owner. The sender must be the collection owner.
1292 fn resource_add(1627 fn resource_add(
1293 sender: T::AccountId,1628 sender: T::AccountId,
1294 collection_id: CollectionId,1629 collection_id: CollectionId,
1319 <PalletNft<T>>::try_mutate_token_aux_property(1654 <PalletNft<T>>::try_mutate_token_aux_property(
1320 collection_id,1655 collection_id,
1321 nft_id,1656 nft_id,
1322 PropertyScope::Rmrk,1657 RMRK_SCOPE,
1323 Self::rmrk_property_key(ResourceId(id))?,1658 Self::get_scoped_property_key(ResourceId(id))?,
1324 |value| -> DispatchResult {1659 |value| -> DispatchResult {
1325 *value = Some(Self::encode_property(&resource_info)?);1660 *value = Some(Self::encode_property_value(&resource_info)?);
13261661
1327 Ok(())1662 Ok(())
1328 },1663 },
1331 Ok(id)1666 Ok(id)
1332 }1667 }
13331668
1669 /// Designate a resource for erasure from an NFT, and remove it if the sender is the token owner.
1670 /// The sender must be the collection owner.
1334 fn resource_remove(1671 fn resource_remove(
1335 sender: T::AccountId,1672 sender: T::AccountId,
1336 collection_id: CollectionId,1673 collection_id: CollectionId,
1341 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1678 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;
1342 ensure!(collection.owner == sender, Error::<T>::NoPermission);1679 ensure!(collection.owner == sender, Error::<T>::NoPermission);
13431680
1344 let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;1681 let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
1345 let scope = PropertyScope::Rmrk;
13461682
1347 let resource = <PalletNft<T>>::token_aux_property((1683 let resource = <PalletNft<T>>::token_aux_property((
1348 collection_id,1684 collection_id,
1349 nft_id,1685 nft_id,
1350 scope,1686 RMRK_SCOPE,
1351 resource_id_key.clone(),1687 resource_id_key.clone(),
1352 ))1688 ))
1353 .ok_or(<Error<T>>::ResourceDoesntExist)?;1689 .ok_or(<Error<T>>::ResourceDoesntExist)?;
13541690
1355 let resource_info: RmrkResourceInfo = Self::decode_property(&resource)?;1691 let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource)?;
13561692
1357 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1693 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
1358 let topmost_owner =1694 let topmost_owner =
1363 <PalletNft<T>>::remove_token_aux_property(1699 <PalletNft<T>>::remove_token_aux_property(
1364 collection_id,1700 collection_id,
1365 nft_id,1701 nft_id,
1366 PropertyScope::Rmrk,1702 RMRK_SCOPE,
1367 Self::rmrk_property_key(ResourceId(resource_id))?,1703 Self::get_scoped_property_key(ResourceId(resource_id))?,
1368 );1704 );
13691705
1370 if let RmrkResourceTypes::Composable(resource) = resource_info.resource {1706 if let RmrkResourceTypes::Composable(resource) = resource_info.resource {
1383 Ok(())1719 Ok(())
1384 }1720 }
13851721
1722 /// Remove a Base ID from an NFT if they are associated.
1723 /// The Base itself is deleted if the number of associated NFTs reaches 0.
1386 fn remove_associated_base_id(1724 fn remove_associated_base_id(
1387 collection_id: CollectionId,1725 collection_id: CollectionId,
1388 nft_id: TokenId,1726 nft_id: TokenId,
1391 <PalletNft<T>>::try_mutate_token_aux_property(1729 <PalletNft<T>>::try_mutate_token_aux_property(
1392 collection_id,1730 collection_id,
1393 nft_id,1731 nft_id,
1394 PropertyScope::Rmrk,1732 RMRK_SCOPE,
1395 Self::rmrk_property_key(AssociatedBases)?,1733 Self::get_scoped_property_key(AssociatedBases)?,
1396 |value| -> DispatchResult {1734 |value| -> DispatchResult {
1397 let mut bases: BasesMap = match value {1735 let mut bases: BasesMap = match value {
1398 Some(value) => Self::decode_property(value)?,1736 Some(value) => Self::decode_property_value(value)?,
1399 None => BasesMap::new(),1737 None => BasesMap::new(),
1400 };1738 };
14011739
1407 }1745 }
1408 }1746 }
14091747
1410 *value = Some(Self::encode_property(&bases)?);1748 *value = Some(Self::encode_property_value(&bases)?);
1411 Ok(())1749 Ok(())
1412 },1750 },
1413 )1751 )
1414 }1752 }
14151753
1754 /// Apply a mutation to a resource stored in the token properties of an NFT.
1416 fn try_mutate_resource_info(1755 fn try_mutate_resource_info(
1417 collection_id: CollectionId,1756 collection_id: CollectionId,
1418 nft_id: TokenId,1757 nft_id: TokenId,
1422 <PalletNft<T>>::try_mutate_token_aux_property(1761 <PalletNft<T>>::try_mutate_token_aux_property(
1423 collection_id,1762 collection_id,
1424 nft_id,1763 nft_id,
1425 PropertyScope::Rmrk,1764 RMRK_SCOPE,
1426 Self::rmrk_property_key(ResourceId(resource_id))?,1765 Self::get_scoped_property_key(ResourceId(resource_id))?,
1427 |value| match value {1766 |value| match value {
1428 Some(value) => {1767 Some(value) => {
1429 let mut resource_info: RmrkResourceInfo = Self::decode_property(value)?;1768 let mut resource_info: RmrkResourceInfo = Self::decode_property_value(value)?;
14301769
1431 f(&mut resource_info)?;1770 f(&mut resource_info)?;
14321771
1433 *value = Self::encode_property(&resource_info)?;1772 *value = Self::encode_property_value(&resource_info)?;
14341773
1435 Ok(())1774 Ok(())
1436 }1775 }
1439 )1778 )
1440 }1779 }
14411780
1781 /// Change the owner of an NFT collection, ensuring that the sender is the current owner.
1442 fn change_collection_owner(1782 fn change_collection_owner(
1443 collection_id: CollectionId,1783 collection_id: CollectionId,
1444 collection_type: misc::CollectionType,1784 collection_type: misc::CollectionType,
1454 collection.save()1794 collection.save()
1455 }1795 }
14561796
1797 /// Ensure that an account is the collection owner/issuer, return an error if not.
1457 pub fn check_collection_owner(1798 pub fn check_collection_owner(
1458 collection: &NonfungibleHandle<T>,1799 collection: &NonfungibleHandle<T>,
1459 account: &T::CrossAccountId,1800 account: &T::CrossAccountId,
1463 .map_err(Self::map_unique_err_to_proxy)1804 .map_err(Self::map_unique_err_to_proxy)
1464 }1805 }
14651806
1807 /// Get the latest yet-unused RMRK collection index from the storage.
1466 pub fn last_collection_idx() -> RmrkCollectionId {1808 pub fn last_collection_idx() -> RmrkCollectionId {
1467 <CollectionIndex<T>>::get()1809 <CollectionIndex<T>>::get()
1468 }1810 }
14691811
1812 /// Get a mapping from a RMRK collection ID to its corresponding Unique collection ID.
1470 pub fn unique_collection_id(1813 pub fn unique_collection_id(
1471 rmrk_collection_id: RmrkCollectionId,1814 rmrk_collection_id: RmrkCollectionId,
1472 ) -> Result<CollectionId, DispatchError> {1815 ) -> Result<CollectionId, DispatchError> {
1473 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1816 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)
1474 .map_err(|_| <Error<T>>::CollectionUnknown.into())1817 .map_err(|_| <Error<T>>::CollectionUnknown.into())
1475 }1818 }
14761819
1820 /// Get a mapping from a Unique collection ID to its RMRK collection ID counterpart, if it exists.
1477 pub fn rmrk_collection_id(1821 pub fn rmrk_collection_id(
1478 unique_collection_id: CollectionId,1822 unique_collection_id: CollectionId,
1479 ) -> Result<RmrkCollectionId, DispatchError> {1823 ) -> Result<RmrkCollectionId, DispatchError> {
1480 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)1824 Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)
1481 }1825 }
14821826
1827 /// Fetch a Unique NFT collection.
1483 pub fn get_nft_collection(1828 pub fn get_nft_collection(
1484 collection_id: CollectionId,1829 collection_id: CollectionId,
1485 ) -> Result<NonfungibleHandle<T>, DispatchError> {1830 ) -> Result<NonfungibleHandle<T>, DispatchError> {
1492 }1837 }
1493 }1838 }
14941839
1840 /// Check if an NFT collection with such an ID exists.
1495 pub fn collection_exists(collection_id: CollectionId) -> bool {1841 pub fn collection_exists(collection_id: CollectionId) -> bool {
1496 <CollectionHandle<T>>::try_get(collection_id).is_ok()1842 <CollectionHandle<T>>::try_get(collection_id).is_ok()
1497 }1843 }
14981844
1845 /// Fetch and decode a RMRK-scoped collection property value in bytes.
1499 pub fn get_collection_property(1846 pub fn get_collection_property(
1500 collection_id: CollectionId,1847 collection_id: CollectionId,
1501 key: RmrkProperty,1848 key: RmrkProperty,
1502 ) -> Result<PropertyValue, DispatchError> {1849 ) -> Result<PropertyValue, DispatchError> {
1503 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1850 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)
1504 .get(&Self::rmrk_property_key(key)?)1851 .get(&Self::get_scoped_property_key(key)?)
1505 .ok_or(<Error<T>>::CollectionUnknown)?1852 .ok_or(<Error<T>>::CollectionUnknown)?
1506 .clone();1853 .clone();
15071854
1508 Ok(collection_property)1855 Ok(collection_property)
1509 }1856 }
15101857
1858 /// Fetch a RMRK-scoped collection property and decode it from bytes into an appropriate type.
1511 pub fn get_collection_property_decoded<V: Decode>(1859 pub fn get_collection_property_decoded<V: Decode>(
1512 collection_id: CollectionId,1860 collection_id: CollectionId,
1513 key: RmrkProperty,1861 key: RmrkProperty,
1514 ) -> Result<V, DispatchError> {1862 ) -> Result<V, DispatchError> {
1515 Self::decode_property(&Self::get_collection_property(collection_id, key)?)1863 Self::decode_property_value(&Self::get_collection_property(collection_id, key)?)
1516 }1864 }
15171865
1866 /// Get the type of a collection stored as a scoped property.
1867 ///
1868 /// RMRK Core proxy differentiates between regular collections as well as RMRK Bases as collections.
1518 pub fn get_collection_type(1869 pub fn get_collection_type(
1519 collection_id: CollectionId,1870 collection_id: CollectionId,
1520 ) -> Result<misc::CollectionType, DispatchError> {1871 ) -> Result<misc::CollectionType, DispatchError> {
1527 })1878 })
1528 }1879 }
15291880
1881 /// Ensure that the type of the collection equals the provided type,
1882 /// otherwise return an error.
1530 pub fn ensure_collection_type(1883 pub fn ensure_collection_type(
1531 collection_id: CollectionId,1884 collection_id: CollectionId,
1532 collection_type: misc::CollectionType,1885 collection_type: misc::CollectionType,
1540 Ok(())1893 Ok(())
1541 }1894 }
15421895
1896 /// Fetch an NFT collection, but make sure it has the appropriate type.
1543 pub fn get_typed_nft_collection(1897 pub fn get_typed_nft_collection(
1544 collection_id: CollectionId,1898 collection_id: CollectionId,
1545 collection_type: misc::CollectionType,1899 collection_type: misc::CollectionType,
1549 Self::get_nft_collection(collection_id)1903 Self::get_nft_collection(collection_id)
1550 }1904 }
15511905
1906 /// Same as [`get_typed_nft_collection`](crate::pallet::Pallet::get_typed_nft_collection),
1907 /// but also return the Unique collection ID.
1552 pub fn get_typed_nft_collection_mapped(1908 pub fn get_typed_nft_collection_mapped(
1553 rmrk_collection_id: RmrkCollectionId,1909 rmrk_collection_id: RmrkCollectionId,
1554 collection_type: misc::CollectionType,1910 collection_type: misc::CollectionType,
1563 Ok((collection, unique_collection_id))1919 Ok((collection, unique_collection_id))
1564 }1920 }
15651921
1922 /// Fetch and decode a RMRK-scoped NFT property value in bytes.
1566 pub fn get_nft_property(1923 pub fn get_nft_property(
1567 collection_id: CollectionId,1924 collection_id: CollectionId,
1568 nft_id: TokenId,1925 nft_id: TokenId,
1569 key: RmrkProperty,1926 key: RmrkProperty,
1570 ) -> Result<PropertyValue, DispatchError> {1927 ) -> Result<PropertyValue, DispatchError> {
1571 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1928 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
1572 .get(&Self::rmrk_property_key(key)?)1929 .get(&Self::get_scoped_property_key(key)?)
1573 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?1930 .ok_or(<Error<T>>::RmrkPropertyIsNotFound)?
1574 .clone();1931 .clone();
15751932
1576 Ok(nft_property)1933 Ok(nft_property)
1577 }1934 }
15781935
1936 /// Fetch a RMRK-scoped NFT property and decode it from bytes into an appropriate type.
1579 pub fn get_nft_property_decoded<V: Decode>(1937 pub fn get_nft_property_decoded<V: Decode>(
1580 collection_id: CollectionId,1938 collection_id: CollectionId,
1581 nft_id: TokenId,1939 nft_id: TokenId,
1582 key: RmrkProperty,1940 key: RmrkProperty,
1583 ) -> Result<V, DispatchError> {1941 ) -> Result<V, DispatchError> {
1584 Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)1942 Self::decode_property_value(&Self::get_nft_property(collection_id, nft_id, key)?)
1585 }1943 }
15861944
1945 /// Check that an NFT exists.
1587 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1946 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {
1588 <TokenData<T>>::contains_key((collection_id, nft_id))1947 <TokenData<T>>::contains_key((collection_id, nft_id))
1589 }1948 }
15901949
1950 /// Get the type of an NFT stored as a scoped property.
1951 ///
1952 /// RMRK Core proxy differentiates between regular NFTs, and RMRK Parts and Themes.
1591 pub fn get_nft_type(1953 pub fn get_nft_type(
1592 collection_id: CollectionId,1954 collection_id: CollectionId,
1593 token_id: TokenId,1955 token_id: TokenId,
1596 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1958 .map_err(|_| <Error<T>>::NoAvailableNftId.into())
1597 }1959 }
15981960
1961 /// Ensure that the type of the NFT equals the provided type, otherwise return an error.
1599 pub fn ensure_nft_type(1962 pub fn ensure_nft_type(
1600 collection_id: CollectionId,1963 collection_id: CollectionId,
1601 token_id: TokenId,1964 token_id: TokenId,
1607 Ok(())1970 Ok(())
1608 }1971 }
16091972
1973 /// Ensure that an account is the owner of the token, either directly
1974 /// or at the top of the nesting hierarchy; return an error if it is not.
1610 pub fn ensure_nft_owner(1975 pub fn ensure_nft_owner(
1611 collection_id: CollectionId,1976 collection_id: CollectionId,
1612 token_id: TokenId,1977 token_id: TokenId,
1627 Ok(())1992 Ok(())
1628 }1993 }
16291994
1995 /// Fetch non-scoped properties of a collection or a token that match the filter keys supplied,
1996 /// or, if None are provided, return all non-scoped properties.
1630 pub fn filter_user_properties<Key, Value, R, Mapper>(1997 pub fn filter_user_properties<Key, Value, R, Mapper>(
1631 collection_id: CollectionId,1998 collection_id: CollectionId,
1632 token_id: Option<TokenId>,1999 token_id: Option<TokenId>,
1672 })2039 })
1673 }2040 }
16742041
2042 /// Get all non-scoped properties from a collection or a token, and apply some transformation,
2043 /// supplied by `mapper`, to each key-value pair.
1675 pub fn iterate_user_properties<Key, Value, R, Mapper>(2044 pub fn iterate_user_properties<Key, Value, R, Mapper>(
1676 collection_id: CollectionId,2045 collection_id: CollectionId,
1677 token_id: Option<TokenId>,2046 token_id: Option<TokenId>,
1699 Ok(properties)2068 Ok(properties)
1700 }2069 }
17012070
2071 /// 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 {2072 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {
1703 map_unique_err_to_proxy! {2073 map_unique_err_to_proxy! {
1704 match err {2074 match err {
modifiedpallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/misc.rs
+++ b/pallets/proxy-rmrk-core/src/misc.rs
@@ -14,9 +14,13 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+//! Miscellaneous helpers and utilities used by the proxy pallet.
+
 use super::*;
 use codec::{Encode, Decode, Error};
 
+/// Match an error to a provided pattern matcher and get
+/// the corresponding error of another type if a match is successful.
 #[macro_export]
 macro_rules! map_unique_err_to_proxy {
     (match $err:ident { $($unique_err_ty:ident :: $unique_err:ident => $proxy_err:ident),+ $(,)? }) => {
@@ -30,8 +34,10 @@
     };
 }
 
-// Utilize the RmrkCore pallet for access to Runtime errors.
+/// Interface to decode some serialized bytes into an arbitrary type `T`,
+/// preferably if these bytes were originally encoded from `T`.
 pub trait RmrkDecode<T: Decode, S> {
+	/// Try to decode self into an arbitrary type `T`.
 	fn decode(&self) -> Result<T, Error>;
 }
 
@@ -43,8 +49,9 @@
 	}
 }
 
-// Utilize the RmrkCore pallet for access to Runtime errors.
+/// Interface to "rebind" - change the limit of a bounded byte vector.
 pub trait RmrkRebind<T, S> {
+	/// Try to change the limit of a bounded byte vector.
 	fn rebind(&self) -> Result<BoundedVec<u8, S>, Error>;
 }
 
@@ -58,12 +65,16 @@
 	}
 }
 
+/// RMRK Base shares functionality with a regular collection, and is thus
+/// stored as one, but they are used for different purposes and need to be differentiated.
 #[derive(Encode, Decode, PartialEq, Eq)]
 pub enum CollectionType {
 	Regular,
 	Base,
 }
 
+/// RMRK Base, being stored as a collection, can have different kinds of tokens,
+/// all except the `Regular` type, which is attributed to `Regular` collection.
 #[derive(Encode, Decode, PartialEq, Eq)]
 pub enum NftType {
 	Regular,
modifiedpallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/property.rs
+++ b/pallets/proxy-rmrk-core/src/property.rs
@@ -14,13 +14,21 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+//! Details of storing and handling RMRK properties.
+
 use super::*;
 use up_data_structs::PropertyScope;
 use core::convert::AsRef;
 
+/// Property prefix for storing resources.
 pub const RESOURCE_ID_PREFIX: &str = "rsid-";
+/// Property prefix for storing custom user-defined properties.
 pub const USER_PROPERTY_PREFIX: &str = "userprop-";
+/// Property scope for RMRK, used to signify that this property
+/// was created and is used by RMRK.
+pub const RMRK_SCOPE: PropertyScope = PropertyScope::Rmrk;
 
+/// Predefined RMRK property keys for storage of RMRK data format on the Unique chain.
 pub enum RmrkProperty<'r> {
 	Metadata,
 	CollectionType,
@@ -49,6 +57,7 @@
 }
 
 impl<'r> RmrkProperty<'r> {
+	/// Convert a predefined RMRK property key enum into string bytes.
 	pub fn to_key<T: Config>(self) -> Result<PropertyKey, Error<T>> {
 		fn get_bytes<T: AsRef<[u8]>>(container: &T) -> &[u8] {
 			container.as_ref()
@@ -94,9 +103,10 @@
 	}
 }
 
+/// Strip a property key of its prefix and RMRK scope.
 pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option<PropertyKey> {
 	let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?;
-	let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?;
+	let key_prefix = RMRK_SCOPE.apply(key_prefix).ok()?;
 
 	key.as_slice()
 		.strip_prefix(key_prefix.as_slice())?
@@ -105,6 +115,7 @@
 		.ok()
 }
 
+/// Check that the key has the prefix.
 pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool {
 	strip_key_prefix(key, prefix).is_some()
 }
modifiedpallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-core/src/rpc.rs
+++ b/pallets/proxy-rmrk-core/src/rpc.rs
@@ -1,9 +1,29 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Realizations of RMRK RPCs (remote procedure calls) related to the Core pallet.
+
 use super::*;
 
+/// Get the latest created collection ID.
 pub fn last_collection_idx<T: Config>() -> Result<RmrkCollectionId, DispatchError> {
 	Ok(<Pallet<T>>::last_collection_idx())
 }
 
+/// Get collection info by ID.
 pub fn collection_by_id<T: Config>(
 	collection_id: RmrkCollectionId,
 ) -> Result<Option<RmrkCollectionInfo<T::AccountId>>, DispatchError> {
@@ -29,6 +49,7 @@
 	}))
 }
 
+/// Get NFT info by collection and NFT IDs.
 pub fn nft_by_id<T: Config>(
 	collection_id: RmrkCollectionId,
 	nft_by_id: RmrkNftId,
@@ -83,6 +104,7 @@
 	}))
 }
 
+/// Get tokens owned by an account in a collection.
 pub fn account_tokens<T: Config>(
 	account_id: T::AccountId,
 	collection_id: RmrkCollectionId,
@@ -116,6 +138,7 @@
 	Ok(tokens)
 }
 
+/// Get tokens nested in an NFT - its direct children (not the children's children).
 pub fn nft_children<T: Config>(
 	collection_id: RmrkCollectionId,
 	nft_id: RmrkNftId,
@@ -152,6 +175,7 @@
 	)
 }
 
+/// Get collection properties, created by the user - not the proxy-specific properties.
 pub fn collection_properties<T: Config>(
 	collection_id: RmrkCollectionId,
 	filter_keys: Option<Vec<RmrkPropertyKey>>,
@@ -174,6 +198,7 @@
 	Ok(properties)
 }
 
+/// Get NFT properties, created by the user - not the proxy-specific properties.
 pub fn nft_properties<T: Config>(
 	collection_id: RmrkCollectionId,
 	nft_id: RmrkNftId,
@@ -199,6 +224,7 @@
 	Ok(properties)
 }
 
+/// Get full information on each resource of an NFT, including pending.
 pub fn nft_resources<T: Config>(
 	collection_id: RmrkCollectionId,
 	nft_id: RmrkNftId,
@@ -226,7 +252,7 @@
 			return None;
 		}
 
-		let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property(&value).ok()?;
+		let resource_info: RmrkResourceInfo = <Pallet<T>>::decode_property_value(&value).ok()?;
 
 		Some(resource_info)
 	})
@@ -235,6 +261,7 @@
 	Ok(resources)
 }
 
+/// Get the priority of a resource in an NFT.
 pub fn nft_resource_priority<T: Config>(
 	collection_id: RmrkCollectionId,
 	nft_id: RmrkNftId,
modifiedpallets/proxy-rmrk-equip/src/benchmarking.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-equip/src/benchmarking.rs
+++ b/pallets/proxy-rmrk-equip/src/benchmarking.rs
@@ -1,3 +1,19 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
 use sp_std::vec;
 
 use frame_benchmarking::{benchmarks, account};
modifiedpallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -14,6 +14,123 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
+//! # RMRK Core Proxy Pallet
+//!
+//! A pallet used as proxy for RMRK Core (<https://rmrk-team.github.io/rmrk-substrate/#/pallets/rmrk-core>).
+//!
+//! - [`Config`]
+//! - [`Call`]
+//! - [`Pallet`]
+//!
+//! ## Overview
+//!
+//! The RMRK Equip Proxy pallet mirrors the functionality of RMRK Equip,
+//! binding its externalities to Unique's own underlying structure.
+//! It is purposed to mimic RMRK Equip exactly, allowing seamless integrations
+//! of solutions based on RMRK.
+//!
+//! RMRK Equip itself contains functionality to equip NFTs, and work with Bases,
+//! Parts, and Themes. See [Proxy Implementation](#proxy-implementation) for details.
+//!
+//! Equip Proxy is responsible for a more specific area of RMRK, and heavily relies on the Core.
+//! For a more foundational description of proxy implementation, please refer to [`pallet_rmrk_core`].
+//!
+//! *Note*, that while RMRK itself is subject to active development and restructuring,
+//! the proxy may be caught temporarily out of date.
+//!
+//! ### What is RMRK?
+//!
+//! RMRK is a set of NFT standards which compose several "NFT 2.0 lego" primitives.
+//! Putting these legos together allows a user to create NFT systems of arbitrary complexity.
+//!
+//! Meaning, RMRK NFTs are dynamic, able to nest into each other and form a hierarchy,
+//! make use of specific changeable and partially shared metadata in the form of resources,
+//! and more.
+//!
+//! Visit RMRK documentation and repositories to learn more:
+//! - Docs: <https://docs.rmrk.app/getting-started/>
+//! - FAQ: <https://coda.io/@rmrk/faq>
+//! - Substrate code repository: <https://github.com/rmrk-team/rmrk-substrate>
+//! - RMRK spec repository: <https://github.com/rmrk-team/rmrk-spec>
+//!
+//! ## Terminology
+//!
+//! For more information on RMRK, see RMRK's own documentation.
+//!
+//! ### Intro to RMRK
+//!
+//! - **Resource:** Additional piece of metadata of an NFT usually serving to add
+//! a piece of media on top of the root metadata (NFT's own), be it a different wing
+//! on the root template bird or something entirely unrelated.
+//!
+//! - **Base:** A list of possible "components" - Parts, a combination of which can
+//! be appended/equipped to/on an NFT.
+//!
+//! - **Part:** Something that, together with other Parts, can constitute an NFT.
+//! Parts are defined in the Base to which they belong. Parts can be either
+//! of the `slot` type or `fixed` type. Slots are intended for equippables.
+//! Note that "part of something" and "Part of a Base" can be easily confused,
+//! and so in this documentation these words are distinguished by the capital letter.
+//!
+//! - **Theme:** Named objects of variable => value pairs which get interpolated into
+//! the Base's `themable` Parts. Themes can hold any value, but are often represented
+//! in RMRK's examples as colors applied to visible Parts.
+//!
+//! ### Peculiarities in Unique
+//!
+//! - **Scoped properties:** Properties that are normally obscured from users.
+//! Their purpose is to contain structured metadata that was not included in the Unique standard
+//! for collections and tokens, meant to be operated on by proxies and other outliers.
+//! Scoped property keys are prefixed with `some-scope:`, where `some-scope` is
+//! an arbitrary keyword, like "rmrk". `:` is considered an unacceptable symbol in user-defined
+//! properties, which, along with other safeguards, makes scoped ones impossible to tamper with.
+//!
+//! - **Auxiliary properties:** A slightly different structure of properties,
+//! trading universality of use for more convenient storage, writes and access.
+//! Meant to be inaccessible to end users.
+//!
+//! ## Proxy Implementation
+//!
+//! An external user is supposed to be able to utilize this proxy as they would
+//! utilize RMRK, and get exactly the same results. Normally, Unique transactions
+//! are off-limits to RMRK collections and tokens, and vice versa. However,
+//! the information stored on chain can be freely interpreted by storage reads and Unique RPCs.
+//!
+//! ### ID Mapping
+//!
+//! RMRK's collections' IDs are counted independently of Unique's and start at 0.
+//! Note that tokens' IDs still start at 1.
+//! The collections themselves, as well as tokens, are stored as Unique collections,
+//! and thus RMRK IDs are mapped to Unique IDs (but not vice versa).
+//!
+//! ### External/Internal Collection Insulation
+//!
+//! A Unique transaction cannot target collections purposed for RMRK,
+//! and they are flagged as `external` to specify that. On the other hand,
+//! due to the mapping, RMRK transactions and RPCs simply cannot reach Unique collections.
+//!
+//! ### Native Properties
+//!
+//! Many of RMRK's native parameters are stored as scoped properties of a collection
+//! or an NFT on the chain. Scoped properties are prefixed with `rmrk:`, where `:`
+//! is an unacceptable symbol in user-defined properties, which, along with other safeguards,
+//! makes them impossible to tamper with.
+//!
+//! ### Collection and NFT Types, or Base, Parts and Themes Handling
+//!
+//! RMRK introduces the concept of a Base, which is a catalogue of Parts,
+//! possible components of an NFT. Due to its similarity with the functionality
+//! of a token collection, a Base is stored and handled as one, and the Base's Parts and Themes
+//! are this collection's NFTs. See [`CollectionType`] and [`NftType`].
+//!
+//! ## Interface
+//!
+//! ### Dispatchables
+//!
+//! - `create_base` - Create a new Base.
+//! - `theme_add` - Add a Theme to a Base.
+//! - `equippable` - Update the array of Collections allowed to be equipped to a Base's specified Slot Part.
+
 #![cfg_attr(not(feature = "std"), no_std)]
 
 use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
@@ -45,15 +162,20 @@
 
 	#[pallet::config]
 	pub trait Config: frame_system::Config + pallet_rmrk_core::Config {
+		/// Overarching event type.
 		type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
+
+		/// The weight information of this pallet.
 		type WeightInfo: WeightInfo;
 	}
 
+	/// Map of a Base ID and a Part ID to an NFT in the Base collection serving as the Part.
 	#[pallet::storage]
 	#[pallet::getter(fn internal_part_id)]
 	pub type InernalPartId<T: Config> =
 		StorageDoubleMap<_, Twox64Concat, CollectionId, Twox64Concat, RmrkPartId, TokenId>;
 
+	/// Checkmark that a Base has a Theme NFT named "default".
 	#[pallet::storage]
 	#[pallet::getter(fn base_has_default_theme)]
 	pub type BaseHasDefaultTheme<T: Config> =
@@ -78,26 +200,36 @@
 
 	#[pallet::error]
 	pub enum Error<T> {
+		/// No permission to perform action.
 		PermissionError,
+		/// Could not find an ID for a Base collection. It is likely there were too many collections created on the chain, causing an overflow.
 		NoAvailableBaseId,
+		/// Could not find a suitable ID for a Part, likely too many Part tokens were created in the Base, causing an overflow
 		NoAvailablePartId,
+		/// Base collection linked to this ID does not exist.
 		BaseDoesntExist,
+		/// No Theme named "default" is associated with the Base.
 		NeedsDefaultThemeFirst,
+		/// Part linked to this ID does not exist.
 		PartDoesntExist,
+		/// Cannot assign equippables to a fixed Part.
 		NoEquippableOnFixedPart,
 	}
 
 	#[pallet::call]
 	impl<T: Config> Pallet<T> {
-		/// Creates a new Base.
-		/// Modeled after [base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)
+		/// Create a new Base.
 		///
-		/// Parameters:
-		/// - origin: Caller, will be assigned as the issuer of the Base
-		/// - base_type: media type, e.g. "svg"
-		/// - symbol: arbitrary client-chosen symbol
-		/// - parts: array of Fixed and Slot parts composing the base, confined in length by
-		///   RmrkPartsLimit
+		/// Modeled after the [Base interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/base.md)
+		///
+		/// # Permissions
+		/// - Anyone - will be assigned as the issuer of the Base.
+		///
+		/// # Arguments:
+		/// - `base_type`: Arbitrary media type, e.g. "svg".
+		/// - `symbol`: Arbitrary client-chosen symbol.
+		/// - `parts`: Array of Fixed and Slot Parts composing the Base,
+		/// confined in length by [`RmrkPartsLimit`](up_data_structs::RmrkPartsLimit).
 		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::create_base(parts.len() as u32))]
 		pub fn create_base(
@@ -131,8 +263,11 @@
 				collection_id,
 				PropertyScope::Rmrk,
 				[
-					<PalletCore<T>>::rmrk_property(CollectionType, &misc::CollectionType::Base)?,
-					<PalletCore<T>>::rmrk_property(BaseType, &base_type)?,
+					<PalletCore<T>>::encode_rmrk_property(
+						CollectionType,
+						&misc::CollectionType::Base,
+					)?,
+					<PalletCore<T>>::encode_rmrk_property(BaseType, &base_type)?,
 				]
 				.into_iter(),
 			)?;
@@ -151,19 +286,21 @@
 			Ok(())
 		}
 
-		/// Adds a Theme to a Base.
-		/// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md)
-		/// Themes are stored in the Themes storage
+		/// Add a Theme to a Base.
 		/// A Theme named "default" is required prior to adding other Themes.
 		///
-		/// Parameters:
-		/// - origin: The caller of the function, must be issuer of the base
-		/// - base_id: The Base containing the Theme to be updated
-		/// - theme: The Theme to add to the Base.  A Theme has a name and properties, which are an
+		/// Modeled after [Themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md).
+		///
+		/// # Permissions:
+		/// - Base issuer
+		///
+		/// # Arguments:
+		/// - `base_id`: Base ID containing the Theme to be updated.
+		/// - `theme`: Theme to add to the Base.  A Theme has a name and properties, which are an
 		///   array of [key, value, inherit].
-		///   - key: arbitrary BoundedString, defined by client
-		///   - value: arbitrary BoundedString, defined by client
-		///   - inherit: optional bool
+		///   - `key`: Arbitrary BoundedString, defined by client.
+		///   - `value`: Arbitrary BoundedString, defined by client.
+		///   - `inherit`: Optional bool.
 		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::theme_add(theme.properties.len() as u32))]
 		pub fn theme_add(
@@ -191,9 +328,9 @@
 				owner,
 				&collection,
 				[
-					<PalletCore<T>>::rmrk_property(TokenType, &NftType::Theme)?,
-					<PalletCore<T>>::rmrk_property(ThemeName, &theme.name)?,
-					<PalletCore<T>>::rmrk_property(ThemeInherit, &theme.inherit)?,
+					<PalletCore<T>>::encode_rmrk_property(TokenType, &NftType::Theme)?,
+					<PalletCore<T>>::encode_rmrk_property(ThemeName, &theme.name)?,
+					<PalletCore<T>>::encode_rmrk_property(ThemeInherit, &theme.inherit)?,
 				]
 				.into_iter(),
 			)
@@ -204,7 +341,7 @@
 					collection_id,
 					token_id,
 					PropertyScope::Rmrk,
-					<PalletCore<T>>::rmrk_property(
+					<PalletCore<T>>::encode_rmrk_property(
 						UserProperty(property.key.as_slice()),
 						&property.value,
 					)?,
@@ -214,6 +351,17 @@
 			Ok(())
 		}
 
+		/// Update the array of Collections allowed to be equipped to a Base's specified Slot Part.
+		///
+		/// Modeled after [equippable interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/equippable.md).
+		///
+		/// # Permissions:
+		/// - Base issuer
+		///
+		/// # Arguments:
+		/// - `base_id`: Base containing the Slot Part to be updated.
+		/// - `part_id`: Slot Part whose Equippable List is being updated.
+		/// - `equippables`: List of equippables that will override the current Equippables list.
 		#[transactional]
 		#[pallet::weight(<SelfWeightOf<T>>::equippable())]
 		pub fn equippable(
@@ -253,7 +401,7 @@
 						base_collection_id,
 						part_id,
 						PropertyScope::Rmrk,
-						<PalletCore<T>>::rmrk_property(EquippableList, &equippables)?,
+						<PalletCore<T>>::encode_rmrk_property(EquippableList, &equippables)?,
 					)?;
 				}
 			}
@@ -266,6 +414,8 @@
 }
 
 impl<T: Config> Pallet<T> {
+	/// Create (or overwrite) a Part in a Base.
+	/// The Part and the Base are represented as an NFT and a Collection.
 	fn create_part(
 		sender: &T::CrossAccountId,
 		collection: &NonfungibleHandle<T>,
@@ -298,7 +448,7 @@
 					collection.id,
 					token_id,
 					PropertyScope::Rmrk,
-					<PalletCore<T>>::rmrk_property(ExternalPartId, &part_id)?,
+					<PalletCore<T>>::encode_rmrk_property(ExternalPartId, &part_id)?,
 				)?;
 
 				token_id
@@ -310,9 +460,9 @@
 			token_id,
 			PropertyScope::Rmrk,
 			[
-				<PalletCore<T>>::rmrk_property(TokenType, &nft_type)?,
-				<PalletCore<T>>::rmrk_property(Src, &src)?,
-				<PalletCore<T>>::rmrk_property(ZIndex, &z_index)?,
+				<PalletCore<T>>::encode_rmrk_property(TokenType, &nft_type)?,
+				<PalletCore<T>>::encode_rmrk_property(Src, &src)?,
+				<PalletCore<T>>::encode_rmrk_property(ZIndex, &z_index)?,
 			]
 			.into_iter(),
 		)?;
@@ -322,13 +472,15 @@
 				collection.id,
 				token_id,
 				PropertyScope::Rmrk,
-				<PalletCore<T>>::rmrk_property(EquippableList, &part.equippable)?,
+				<PalletCore<T>>::encode_rmrk_property(EquippableList, &part.equippable)?,
 			)?;
 		}
 
 		Ok(())
 	}
 
+	/// Ensure that the collection under the Base ID is a Base collection,
+	/// and fetch it.
 	fn get_base(base_id: CollectionId) -> Result<NonfungibleHandle<T>, DispatchError> {
 		let collection =
 			<PalletCore<T>>::get_typed_nft_collection(base_id, misc::CollectionType::Base)
modifiedpallets/proxy-rmrk-equip/src/rpc.rsdiffbeforeafterboth
--- a/pallets/proxy-rmrk-equip/src/rpc.rs
+++ b/pallets/proxy-rmrk-equip/src/rpc.rs
@@ -1,7 +1,26 @@
+// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.
+// This file is part of Unique Network.
+
+// Unique Network is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Unique Network is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
+
+//! Realizations of RMRK RPCs (remote procedure calls) related to the Equip pallet.
+
 use super::*;
 use pallet_rmrk_core::{misc, property::*};
 use sp_std::vec::Vec;
 
+/// Get base info by its ID.
 pub fn base<T: Config>(
 	base_id: RmrkBaseId,
 ) -> Result<Option<RmrkBaseInfo<T::AccountId>>, DispatchError> {
@@ -22,6 +41,7 @@
 	}))
 }
 
+/// Get all parts of a base.
 pub fn base_parts<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkPartType>, DispatchError> {
 	use pallet_common::CommonCollectionOperations;
 
@@ -93,6 +113,7 @@
 	Ok(parts)
 }
 
+/// Get the theme names belonging to a base.
 pub fn theme_names<T: Config>(base_id: RmrkBaseId) -> Result<Vec<RmrkThemeName>, DispatchError> {
 	use pallet_common::CommonCollectionOperations;
 
@@ -124,6 +145,7 @@
 	Ok(theme_names)
 }
 
+/// Get theme info, including properties, optionally limited to the provided keys.
 pub fn theme<T: Config>(
 	base_id: RmrkBaseId,
 	theme_name: RmrkThemeName,
modifiedprimitives/rmrk-traits/src/resource.rsdiffbeforeafterboth
--- a/primitives/rmrk-traits/src/resource.rs
+++ b/primitives/rmrk-traits/src/resource.rs
@@ -151,13 +151,13 @@
 		"#)
 )]
 pub struct ResourceInfo<BoundedString, BoundedParts> {
-	/// id is a 5-character string of reasonable uniqueness.
-	/// The combination of base ID and resource id should be unique across the entire RMRK
-	/// ecosystem which
+	/// ID is a unique identifier for a resource across all those of a single NFT.
+	/// The combination of a collection ID, an NFT ID, and the resource ID must be
+	/// unique across the entire RMRK ecosystem.
 	//#[cfg_attr(feature = "std", serde(with = "serialize::vec"))]
 	pub id: ResourceId,
 
-	/// Resource
+	/// Resource type and the accordingly structured data stored
 	pub resource: ResourceTypes<BoundedString, BoundedParts>,
 
 	/// If resource is sent to non-rootowned NFT, pending will be false and need to be accepted