difftreelog
doc: rmrk proxies
in: master
8 files changed
pallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth1use sp_std::vec;23use frame_benchmarking::{benchmarks, account};4use frame_system::RawOrigin;5use frame_support::{6 traits::{Currency, Get},7 BoundedVec,8};9use sp_runtime::Permill;1011use up_data_structs::*;1213use super::*;1415const SEED: u32 = 1;1617fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {18 vec![b'A'; S::get() as usize].try_into().expect("size == S")19}2021fn create_u32_array<S: Get<u32>>() -> BoundedVec<u32, S> {22 vec![0; S::get() as usize].try_into().expect("size == S")23}2425fn create_basic_resource() -> RmrkBasicResource {26 RmrkBasicResource {27 src: Some(create_data()),28 metadata: Some(create_data()),29 license: Some(create_data()),30 thumb: Some(create_data()),31 }32}3334fn create_composable_resource() -> RmrkComposableResource {35 RmrkComposableResource {36 parts: create_u32_array(),37 base: 100,38 src: Some(create_data()),39 metadata: Some(create_data()),40 license: Some(create_data()),41 thumb: Some(create_data()),42 }43}4445fn create_slot_resource() -> RmrkSlotResource {46 RmrkSlotResource {47 base: 100,48 slot: 200,49 src: Some(create_data()),50 metadata: Some(create_data()),51 license: Some(create_data()),52 thumb: Some(create_data()),53 }54}5556fn create_max_resource_types_array<S: Get<u32>>(num: usize) -> BoundedVec<RmrkResourceTypes, S> {57 vec![RmrkResourceTypes::Composable(create_composable_resource()); num]58 .try_into()59 .expect("num <= S")60}6162fn create_max_collection<T: Config>(owner: &T::AccountId) -> DispatchResult {63 <T as pallet_common::Config>::Currency::deposit_creating(64 owner,65 T::CollectionCreationPrice::get(),66 );6768 let metadata = create_data();69 let max = None;70 let symbol = create_data();7172 <Pallet<T>>::create_collection(73 RawOrigin::Signed(owner.clone()).into(),74 metadata,75 max,76 symbol,77 )78}7980fn create_nft<T: Config>(owner: &T::AccountId, collection_id: RmrkCollectionId) -> DispatchResult {81 let royalty_recipient = Some(owner.clone());82 let royalty_amount = Some(Permill::from_percent(25));83 let metadata = create_data();84 let transferable = true;8586 <Pallet<T>>::mint_nft(87 RawOrigin::Signed(owner.clone()).into(),88 None,89 collection_id,90 royalty_recipient,91 royalty_amount,92 metadata,93 transferable,94 None,95 )96}9798struct NftBuilder {99 collection_id: RmrkCollectionId,100 current_nft_id: RmrkNftId,101}102103impl NftBuilder {104 fn new(collection_id: RmrkCollectionId) -> Self {105 Self {106 collection_id,107 current_nft_id: 0,108 }109 }110111 fn build<T: Config>(&mut self, owner: &T::AccountId) -> Result<RmrkNftId, DispatchError> {112 create_nft::<T>(owner, self.collection_id)?;113 self.current_nft_id += 1;114115 Ok(self.current_nft_id)116 }117118 fn build_tower<T: Config>(119 &mut self,120 owner: &T::AccountId,121 height: u32,122 ) -> Result<(RmrkNftId, RmrkNftId), DispatchError> {123 self.build::<T>(owner)?;124125 let root_nft_id = self.current_nft_id;126 let mut prev_nft_id = root_nft_id;127128 for _ in 0..height {129 self.build::<T>(owner)?;130131 let new_owner =132 <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(133 self.collection_id,134 prev_nft_id,135 );136137 <Pallet<T>>::send(138 RawOrigin::Signed(owner.clone()).into(),139 self.collection_id,140 self.current_nft_id,141 new_owner,142 )?;143144 prev_nft_id = self.current_nft_id;145 }146147 let deepest_nft_id = self.current_nft_id;148149 Ok((root_nft_id, deepest_nft_id))150 }151152 fn build_wide_tree<T: Config>(153 &mut self,154 owner: &T::AccountId,155 width: u32,156 ) -> Result<RmrkNftId, DispatchError> {157 self.build::<T>(owner)?;158159 let root_nft_id = self.current_nft_id;160161 let root_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(162 self.collection_id,163 root_nft_id,164 );165166 for _ in 0..width {167 self.build::<T>(owner)?;168169 <Pallet<T>>::send(170 RawOrigin::Signed(owner.clone()).into(),171 self.collection_id,172 self.current_nft_id,173 root_owner.clone(),174 )?;175 }176177 Ok(root_nft_id)178 }179}180181benchmarks! {182 create_collection {183 let caller = account("caller", 0, SEED);184 <T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());185 let metadata = create_data();186 let max = None;187 let symbol = create_data();188 }: _(RawOrigin::Signed(caller), metadata, max, symbol)189190 destroy_collection {191 let caller = account("caller", 0, SEED);192193 create_max_collection::<T>(&caller)?;194 let collection_id = 0;195 }: _(RawOrigin::Signed(caller), collection_id)196197 change_collection_issuer {198 let caller: T::AccountId = account("caller", 0, SEED);199200 create_max_collection::<T>(&caller)?;201 let collection_id = 0;202203 let new_owner: T::AccountId = account("new_owner", 0, SEED);204205 let new_owner_source = T::Lookup::unlookup(new_owner);206 }: _(RawOrigin::Signed(caller), collection_id, new_owner_source)207208 lock_collection {209 let caller: T::AccountId = account("caller", 0, SEED);210211 create_max_collection::<T>(&caller)?;212 let collection_id = 0;213 }: _(RawOrigin::Signed(caller), collection_id)214215 mint_nft {216 let b in 0..100;217218 let caller: T::AccountId = account("caller", 0, SEED);219220 create_max_collection::<T>(&caller)?;221 let collection_id = 0;222 let owner = caller.clone();223224 let royalty_recipient = Some(caller.clone());225 let royalty_amount = Some(Permill::from_percent(25));226 let metadata = create_data();227 let transferable = true;228 }: _(229 RawOrigin::Signed(caller),230 None,231 collection_id,232 royalty_recipient,233 royalty_amount,234 metadata,235 transferable,236 Some(create_max_resource_types_array(b as usize))237 )238239 burn_nft {240 let b in 0..200;241242 let caller: T::AccountId = account("caller", 0, SEED);243 create_max_collection::<T>(&caller)?;244 let collection_id = 0;245246 let mut nft_builder = NftBuilder::new(collection_id);247 let root_nft_id = nft_builder.build_wide_tree::<T>(&caller, b)?;248 let max_burns = b + 1;249 }: _(250 RawOrigin::Signed(caller),251 collection_id,252 root_nft_id,253 max_burns254 )255256 send {257 let caller: T::AccountId = account("caller", 0, SEED);258 create_max_collection::<T>(&caller)?;259 let collection_id = 0;260261 let mut nft_builder = NftBuilder::new(collection_id);262 let src_nft_id = nft_builder.build::<T>(&caller)?;263 let (_, target_nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 2)?;264 }: _(265 RawOrigin::Signed(caller),266 collection_id,267 src_nft_id,268 <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(collection_id, target_nft_id)269 )270271 accept_nft {272 let caller: T::AccountId = account("caller", 0, SEED);273 let sender: T::AccountId = account("sender", 0, SEED);274275 create_max_collection::<T>(&sender)?;276 let src_collection_id = 0;277278 create_max_collection::<T>(&caller)?;279 let target_collection_id = 1;280281 let mut src_nft_builder = NftBuilder::new(src_collection_id);282 let src_nft_id = src_nft_builder.build::<T>(&sender)?;283284 let mut target_nft_builder = NftBuilder::new(target_collection_id);285 let fake_target_nft_id = target_nft_builder.build::<T>(&caller)?;286 let (_, target_nft_id) = target_nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;287288 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(289 target_collection_id,290 fake_target_nft_id291 );292293 let actual_new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(294 target_collection_id,295 target_nft_id296 );297298 <Pallet<T>>::send(299 RawOrigin::Signed(sender.clone()).into(),300 src_collection_id,301 src_nft_id,302 new_owner,303 )?;304 }: _(305 RawOrigin::Signed(caller),306 src_collection_id,307 src_nft_id,308 actual_new_owner309 )310311 reject_nft {312 let caller: T::AccountId = account("caller", 0, SEED);313 let sender: T::AccountId = account("sender", 0, SEED);314315 create_max_collection::<T>(&sender)?;316 let src_collection_id = 0;317318 create_max_collection::<T>(&caller)?;319 let target_collection_id = 1;320321 let mut src_nft_builder = NftBuilder::new(src_collection_id);322 let (src_root_nft_id, _) = src_nft_builder.build_tower::<T>(&sender, NESTING_BUDGET - 1)?;323324 let mut target_nft_builder = NftBuilder::new(target_collection_id);325 let target_nft_id = target_nft_builder.build::<T>(&caller)?;326327 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(328 target_collection_id,329 target_nft_id330 );331332 <Pallet<T>>::send(333 RawOrigin::Signed(sender.clone()).into(),334 src_collection_id,335 src_root_nft_id,336 new_owner,337 )?;338 }: _(339 RawOrigin::Signed(caller),340 src_collection_id,341 src_root_nft_id342 )343344 set_property {345 let caller: T::AccountId = account("caller", 0, SEED);346 create_max_collection::<T>(&caller)?;347 let collection_id = 0;348349 let mut nft_builder = NftBuilder::new(collection_id);350 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;351352 let key = create_data();353 let value = create_data();354 }: _(355 RawOrigin::Signed(caller),356 collection_id,357 Some(nft_id),358 key,359 value360 )361362 set_priority {363 let caller: T::AccountId = account("caller", 0, SEED);364 create_max_collection::<T>(&caller)?;365 let collection_id = 0;366367 let mut nft_builder = NftBuilder::new(collection_id);368 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;369 let priorities = create_u32_array();370 }: _(371 RawOrigin::Signed(caller),372 collection_id,373 nft_id,374 priorities375 )376377 add_basic_resource {378 let caller: T::AccountId = account("caller", 0, SEED);379380 create_max_collection::<T>(&caller)?;381 let collection_id = 0;382383 let mut nft_builder = NftBuilder::new(collection_id);384 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;385 let resource = create_basic_resource();386 }: _(387 RawOrigin::Signed(caller),388 collection_id,389 nft_id,390 resource391 )392393 add_composable_resource {394 let caller: T::AccountId = account("caller", 0, SEED);395396 create_max_collection::<T>(&caller)?;397 let collection_id = 0;398399 let mut nft_builder = NftBuilder::new(collection_id);400 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;401 let resource = create_composable_resource();402 }: _(403 RawOrigin::Signed(caller),404 collection_id,405 nft_id,406 resource407 )408409 add_slot_resource {410 let caller: T::AccountId = account("caller", 0, SEED);411412 create_max_collection::<T>(&caller)?;413 let collection_id = 0;414415 let mut nft_builder = NftBuilder::new(collection_id);416 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;417 let resource = create_slot_resource();418 }: _(419 RawOrigin::Signed(caller),420 collection_id,421 nft_id,422 resource423 )424425 remove_resource {426 let caller: T::AccountId = account("caller", 0, SEED);427428 create_max_collection::<T>(&caller)?;429 let collection_id = 0;430431 let mut nft_builder = NftBuilder::new(collection_id);432 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;433 let resource = create_basic_resource();434435 <Pallet<T>>::add_basic_resource(436 RawOrigin::Signed(caller.clone()).into(),437 collection_id,438 nft_id,439 resource440 )?;441442 let resource_id = 0;443 }: _(444 RawOrigin::Signed(caller),445 collection_id,446 nft_id,447 resource_id448 )449450 accept_resource {451 let caller: T::AccountId = account("caller", 0, SEED);452 let admin: T::AccountId = account("admin", 0, SEED);453454 create_max_collection::<T>(&admin)?;455 let collection_id = 0;456457 let mut nft_builder = NftBuilder::new(collection_id);458 let root_nft_id = 1;459 let (_, nested_nft_id) = nft_builder.build_tower::<T>(&admin, NESTING_BUDGET - 1)?;460 let resource = create_basic_resource();461462 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::AccountId(caller.clone());463464 <Pallet<T>>::send(465 RawOrigin::Signed(admin.clone()).into(),466 collection_id,467 root_nft_id,468 new_owner,469 )?;470471 <Pallet<T>>::add_basic_resource(472 RawOrigin::Signed(admin.clone()).into(),473 collection_id,474 nested_nft_id,475 resource476 )?;477478 let resource_id = 0;479 }: _(480 RawOrigin::Signed(caller),481 collection_id,482 nested_nft_id,483 resource_id484 )485486 accept_resource_removal {487 let caller: T::AccountId = account("caller", 0, SEED);488 let admin: T::AccountId = account("admin", 0, SEED);489490 create_max_collection::<T>(&admin)?;491 let collection_id = 0;492493 let mut nft_builder = NftBuilder::new(collection_id);494 let root_nft_id = 1;495 let (_, nested_nft_id) = nft_builder.build_tower::<T>(&admin, NESTING_BUDGET - 1)?;496 let resource = create_basic_resource();497498 <Pallet<T>>::add_basic_resource(499 RawOrigin::Signed(admin.clone()).into(),500 collection_id,501 nested_nft_id,502 resource503 )?;504505 let resource_id = 0;506507 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::AccountId(caller.clone());508509 <Pallet<T>>::send(510 RawOrigin::Signed(admin.clone()).into(),511 collection_id,512 root_nft_id,513 new_owner,514 )?;515516 <Pallet<T>>::remove_resource(517 RawOrigin::Signed(admin).into(),518 collection_id,519 nested_nft_id,520 resource_id521 )?;522 }: _(523 RawOrigin::Signed(caller),524 collection_id,525 nested_nft_id,526 resource_id527 )528}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use sp_std::vec;1819use frame_benchmarking::{benchmarks, account};20use frame_system::RawOrigin;21use frame_support::{22 traits::{Currency, Get},23 BoundedVec,24};25use sp_runtime::Permill;2627use up_data_structs::*;2829use super::*;3031const SEED: u32 = 1;3233fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {34 vec![b'A'; S::get() as usize].try_into().expect("size == S")35}3637fn create_u32_array<S: Get<u32>>() -> BoundedVec<u32, S> {38 vec![0; S::get() as usize].try_into().expect("size == S")39}4041fn create_basic_resource() -> RmrkBasicResource {42 RmrkBasicResource {43 src: Some(create_data()),44 metadata: Some(create_data()),45 license: Some(create_data()),46 thumb: Some(create_data()),47 }48}4950fn create_composable_resource() -> RmrkComposableResource {51 RmrkComposableResource {52 parts: create_u32_array(),53 base: 100,54 src: Some(create_data()),55 metadata: Some(create_data()),56 license: Some(create_data()),57 thumb: Some(create_data()),58 }59}6061fn create_slot_resource() -> RmrkSlotResource {62 RmrkSlotResource {63 base: 100,64 slot: 200,65 src: Some(create_data()),66 metadata: Some(create_data()),67 license: Some(create_data()),68 thumb: Some(create_data()),69 }70}7172fn create_max_resource_types_array<S: Get<u32>>(num: usize) -> BoundedVec<RmrkResourceTypes, S> {73 vec![RmrkResourceTypes::Composable(create_composable_resource()); num]74 .try_into()75 .expect("num <= S")76}7778fn create_max_collection<T: Config>(owner: &T::AccountId) -> DispatchResult {79 <T as pallet_common::Config>::Currency::deposit_creating(80 owner,81 T::CollectionCreationPrice::get(),82 );8384 let metadata = create_data();85 let max = None;86 let symbol = create_data();8788 <Pallet<T>>::create_collection(89 RawOrigin::Signed(owner.clone()).into(),90 metadata,91 max,92 symbol,93 )94}9596fn create_nft<T: Config>(owner: &T::AccountId, collection_id: RmrkCollectionId) -> DispatchResult {97 let royalty_recipient = Some(owner.clone());98 let royalty_amount = Some(Permill::from_percent(25));99 let metadata = create_data();100 let transferable = true;101102 <Pallet<T>>::mint_nft(103 RawOrigin::Signed(owner.clone()).into(),104 None,105 collection_id,106 royalty_recipient,107 royalty_amount,108 metadata,109 transferable,110 None,111 )112}113114struct NftBuilder {115 collection_id: RmrkCollectionId,116 current_nft_id: RmrkNftId,117}118119impl NftBuilder {120 fn new(collection_id: RmrkCollectionId) -> Self {121 Self {122 collection_id,123 current_nft_id: 0,124 }125 }126127 fn build<T: Config>(&mut self, owner: &T::AccountId) -> Result<RmrkNftId, DispatchError> {128 create_nft::<T>(owner, self.collection_id)?;129 self.current_nft_id += 1;130131 Ok(self.current_nft_id)132 }133134 fn build_tower<T: Config>(135 &mut self,136 owner: &T::AccountId,137 height: u32,138 ) -> Result<(RmrkNftId, RmrkNftId), DispatchError> {139 self.build::<T>(owner)?;140141 let root_nft_id = self.current_nft_id;142 let mut prev_nft_id = root_nft_id;143144 for _ in 0..height {145 self.build::<T>(owner)?;146147 let new_owner =148 <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(149 self.collection_id,150 prev_nft_id,151 );152153 <Pallet<T>>::send(154 RawOrigin::Signed(owner.clone()).into(),155 self.collection_id,156 self.current_nft_id,157 new_owner,158 )?;159160 prev_nft_id = self.current_nft_id;161 }162163 let deepest_nft_id = self.current_nft_id;164165 Ok((root_nft_id, deepest_nft_id))166 }167168 fn build_wide_tree<T: Config>(169 &mut self,170 owner: &T::AccountId,171 width: u32,172 ) -> Result<RmrkNftId, DispatchError> {173 self.build::<T>(owner)?;174175 let root_nft_id = self.current_nft_id;176177 let root_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(178 self.collection_id,179 root_nft_id,180 );181182 for _ in 0..width {183 self.build::<T>(owner)?;184185 <Pallet<T>>::send(186 RawOrigin::Signed(owner.clone()).into(),187 self.collection_id,188 self.current_nft_id,189 root_owner.clone(),190 )?;191 }192193 Ok(root_nft_id)194 }195}196197benchmarks! {198 create_collection {199 let caller = account("caller", 0, SEED);200 <T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());201 let metadata = create_data();202 let max = None;203 let symbol = create_data();204 }: _(RawOrigin::Signed(caller), metadata, max, symbol)205206 destroy_collection {207 let caller = account("caller", 0, SEED);208209 create_max_collection::<T>(&caller)?;210 let collection_id = 0;211 }: _(RawOrigin::Signed(caller), collection_id)212213 change_collection_issuer {214 let caller: T::AccountId = account("caller", 0, SEED);215216 create_max_collection::<T>(&caller)?;217 let collection_id = 0;218219 let new_owner: T::AccountId = account("new_owner", 0, SEED);220221 let new_owner_source = T::Lookup::unlookup(new_owner);222 }: _(RawOrigin::Signed(caller), collection_id, new_owner_source)223224 lock_collection {225 let caller: T::AccountId = account("caller", 0, SEED);226227 create_max_collection::<T>(&caller)?;228 let collection_id = 0;229 }: _(RawOrigin::Signed(caller), collection_id)230231 mint_nft {232 let b in 0..100;233234 let caller: T::AccountId = account("caller", 0, SEED);235236 create_max_collection::<T>(&caller)?;237 let collection_id = 0;238 let owner = caller.clone();239240 let royalty_recipient = Some(caller.clone());241 let royalty_amount = Some(Permill::from_percent(25));242 let metadata = create_data();243 let transferable = true;244 }: _(245 RawOrigin::Signed(caller),246 None,247 collection_id,248 royalty_recipient,249 royalty_amount,250 metadata,251 transferable,252 Some(create_max_resource_types_array(b as usize))253 )254255 burn_nft {256 let b in 0..200;257258 let caller: T::AccountId = account("caller", 0, SEED);259 create_max_collection::<T>(&caller)?;260 let collection_id = 0;261262 let mut nft_builder = NftBuilder::new(collection_id);263 let root_nft_id = nft_builder.build_wide_tree::<T>(&caller, b)?;264 let max_burns = b + 1;265 }: _(266 RawOrigin::Signed(caller),267 collection_id,268 root_nft_id,269 max_burns270 )271272 send {273 let caller: T::AccountId = account("caller", 0, SEED);274 create_max_collection::<T>(&caller)?;275 let collection_id = 0;276277 let mut nft_builder = NftBuilder::new(collection_id);278 let src_nft_id = nft_builder.build::<T>(&caller)?;279 let (_, target_nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 2)?;280 }: _(281 RawOrigin::Signed(caller),282 collection_id,283 src_nft_id,284 <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(collection_id, target_nft_id)285 )286287 accept_nft {288 let caller: T::AccountId = account("caller", 0, SEED);289 let sender: T::AccountId = account("sender", 0, SEED);290291 create_max_collection::<T>(&sender)?;292 let src_collection_id = 0;293294 create_max_collection::<T>(&caller)?;295 let target_collection_id = 1;296297 let mut src_nft_builder = NftBuilder::new(src_collection_id);298 let src_nft_id = src_nft_builder.build::<T>(&sender)?;299300 let mut target_nft_builder = NftBuilder::new(target_collection_id);301 let fake_target_nft_id = target_nft_builder.build::<T>(&caller)?;302 let (_, target_nft_id) = target_nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;303304 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(305 target_collection_id,306 fake_target_nft_id307 );308309 let actual_new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(310 target_collection_id,311 target_nft_id312 );313314 <Pallet<T>>::send(315 RawOrigin::Signed(sender.clone()).into(),316 src_collection_id,317 src_nft_id,318 new_owner,319 )?;320 }: _(321 RawOrigin::Signed(caller),322 src_collection_id,323 src_nft_id,324 actual_new_owner325 )326327 reject_nft {328 let caller: T::AccountId = account("caller", 0, SEED);329 let sender: T::AccountId = account("sender", 0, SEED);330331 create_max_collection::<T>(&sender)?;332 let src_collection_id = 0;333334 create_max_collection::<T>(&caller)?;335 let target_collection_id = 1;336337 let mut src_nft_builder = NftBuilder::new(src_collection_id);338 let (src_root_nft_id, _) = src_nft_builder.build_tower::<T>(&sender, NESTING_BUDGET - 1)?;339340 let mut target_nft_builder = NftBuilder::new(target_collection_id);341 let target_nft_id = target_nft_builder.build::<T>(&caller)?;342343 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::CollectionAndNftTuple(344 target_collection_id,345 target_nft_id346 );347348 <Pallet<T>>::send(349 RawOrigin::Signed(sender.clone()).into(),350 src_collection_id,351 src_root_nft_id,352 new_owner,353 )?;354 }: _(355 RawOrigin::Signed(caller),356 src_collection_id,357 src_root_nft_id358 )359360 set_property {361 let caller: T::AccountId = account("caller", 0, SEED);362 create_max_collection::<T>(&caller)?;363 let collection_id = 0;364365 let mut nft_builder = NftBuilder::new(collection_id);366 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;367368 let key = create_data();369 let value = create_data();370 }: _(371 RawOrigin::Signed(caller),372 collection_id,373 Some(nft_id),374 key,375 value376 )377378 set_priority {379 let caller: T::AccountId = account("caller", 0, SEED);380 create_max_collection::<T>(&caller)?;381 let collection_id = 0;382383 let mut nft_builder = NftBuilder::new(collection_id);384 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;385 let priorities = create_u32_array();386 }: _(387 RawOrigin::Signed(caller),388 collection_id,389 nft_id,390 priorities391 )392393 add_basic_resource {394 let caller: T::AccountId = account("caller", 0, SEED);395396 create_max_collection::<T>(&caller)?;397 let collection_id = 0;398399 let mut nft_builder = NftBuilder::new(collection_id);400 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;401 let resource = create_basic_resource();402 }: _(403 RawOrigin::Signed(caller),404 collection_id,405 nft_id,406 resource407 )408409 add_composable_resource {410 let caller: T::AccountId = account("caller", 0, SEED);411412 create_max_collection::<T>(&caller)?;413 let collection_id = 0;414415 let mut nft_builder = NftBuilder::new(collection_id);416 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;417 let resource = create_composable_resource();418 }: _(419 RawOrigin::Signed(caller),420 collection_id,421 nft_id,422 resource423 )424425 add_slot_resource {426 let caller: T::AccountId = account("caller", 0, SEED);427428 create_max_collection::<T>(&caller)?;429 let collection_id = 0;430431 let mut nft_builder = NftBuilder::new(collection_id);432 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;433 let resource = create_slot_resource();434 }: _(435 RawOrigin::Signed(caller),436 collection_id,437 nft_id,438 resource439 )440441 remove_resource {442 let caller: T::AccountId = account("caller", 0, SEED);443444 create_max_collection::<T>(&caller)?;445 let collection_id = 0;446447 let mut nft_builder = NftBuilder::new(collection_id);448 let (_, nft_id) = nft_builder.build_tower::<T>(&caller, NESTING_BUDGET - 1)?;449 let resource = create_basic_resource();450451 <Pallet<T>>::add_basic_resource(452 RawOrigin::Signed(caller.clone()).into(),453 collection_id,454 nft_id,455 resource456 )?;457458 let resource_id = 0;459 }: _(460 RawOrigin::Signed(caller),461 collection_id,462 nft_id,463 resource_id464 )465466 accept_resource {467 let caller: T::AccountId = account("caller", 0, SEED);468 let admin: T::AccountId = account("admin", 0, SEED);469470 create_max_collection::<T>(&admin)?;471 let collection_id = 0;472473 let mut nft_builder = NftBuilder::new(collection_id);474 let root_nft_id = 1;475 let (_, nested_nft_id) = nft_builder.build_tower::<T>(&admin, NESTING_BUDGET - 1)?;476 let resource = create_basic_resource();477478 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::AccountId(caller.clone());479480 <Pallet<T>>::send(481 RawOrigin::Signed(admin.clone()).into(),482 collection_id,483 root_nft_id,484 new_owner,485 )?;486487 <Pallet<T>>::add_basic_resource(488 RawOrigin::Signed(admin.clone()).into(),489 collection_id,490 nested_nft_id,491 resource492 )?;493494 let resource_id = 0;495 }: _(496 RawOrigin::Signed(caller),497 collection_id,498 nested_nft_id,499 resource_id500 )501502 accept_resource_removal {503 let caller: T::AccountId = account("caller", 0, SEED);504 let admin: T::AccountId = account("admin", 0, SEED);505506 create_max_collection::<T>(&admin)?;507 let collection_id = 0;508509 let mut nft_builder = NftBuilder::new(collection_id);510 let root_nft_id = 1;511 let (_, nested_nft_id) = nft_builder.build_tower::<T>(&admin, NESTING_BUDGET - 1)?;512 let resource = create_basic_resource();513514 <Pallet<T>>::add_basic_resource(515 RawOrigin::Signed(admin.clone()).into(),516 collection_id,517 nested_nft_id,518 resource519 )?;520521 let resource_id = 0;522523 let new_owner = <RmrkAccountIdOrCollectionNftTuple<T::AccountId>>::AccountId(caller.clone());524525 <Pallet<T>>::send(526 RawOrigin::Signed(admin.clone()).into(),527 collection_id,528 root_nft_id,529 new_owner,530 )?;531532 <Pallet<T>>::remove_resource(533 RawOrigin::Signed(admin).into(),534 collection_id,535 nested_nft_id,536 resource_id537 )?;538 }: _(539 RawOrigin::Signed(caller),540 collection_id,541 nested_nft_id,542 resource_id543 )544}pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -14,6 +14,100 @@
// 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 Core Proxy pallet mirrors the functionality of RMRK Core,
+//! binding its externalities to Unique's own underlying structure.
+//! It is purposed to mimic RMRK Core exactly, allowing seamless integrations
+//! of solutions based on RMRK.
+//!
+//! RMRK Core itself contains essential functionality for RMRK's nested and
+//! multi-resourced NFTs.
+//!
+//! *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>
+//!
+//! ## 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 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 proeprties, which, along with other safeguards,
+//! makes them impossible to tamper with.
+//!
+//! ### Collection and NFT Types
+//!
+//! RMRK introduces the concept of a Base, which is a catalgoue 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 the collection's NFTs. See [`CollectionType`](pallet_rmrk_core::misc::CollectionType) and
+//! [`NftType`](pallet_rmrk_core::misc::NftType).
+//!
+//! ## Interface
+//!
+//! ### Dispatchables
+//!
+//! - `create_collection` - Create a new collection of NFTs.
+//! - `destroy_collection` - Destroy a collection.
+//! - `change_collection_issuer` - Change the issuer of a collection.
+//! Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
+//! - `lock_collection` - "Lock" the collection and prevent new token creation. **Cannot be undone.**
+//! - `mint_nft` - Mint an NFT in a specified collection.
+//! - `burn_nft` - Burn an NFT, destroying it and its nested tokens.
+//! - `send` - Transfer an NFT from an account/NFT A to another account/NFT B.
+//! - `accept_nft` - Accept an NFT sent from another account to self or an owned NFT.
+//! - `reject_nft` - Reject an NFT sent from another account to self or owned NFT and **burn it**.
+//! - `accept_resource` - Accept the addition of a newly created pending resource to an existing NFT.
+//! - `accept_resource_removal` - Accept the removal of a removal-pending resource from an NFT.
+//! - `set_property` - Add or edit a custom user property of a token or a collection.
+//! - `set_priority` - Set a different order of resource priorities for an NFT.
+//! - `add_basic_resource` - Create and set/propose a basic resource for an NFT.
+//! - `add_composable_resource` - Create and set/propose a composable resource for an NFT.
+//! - `add_slot_resource` - Create and set/propose a slot resource for an NFT.
+//! - `remove_resource` - Remove and erase a resource from an NFT.
+
#![cfg_attr(not(feature = "std"), no_std)]
use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};
@@ -49,6 +143,7 @@
use RmrkProperty::*;
+/// Maximum number of levels of depth in the token nesting tree.
pub const NESTING_BUDGET: u32 = 5;
type PendingTarget = (CollectionId, TokenId);
@@ -66,14 +161,19 @@
pub trait Config:
frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::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;
}
+ /// Latest yet-unused collection ID.
#[pallet::storage]
#[pallet::getter(fn collection_index)]
pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;
+ /// Mapping from RMRK collection ID to Unique's.
#[pallet::storage]
pub type UniqueCollectionId<T: Config> =
StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;
@@ -159,34 +259,66 @@
#[pallet::error]
pub enum Error<T> {
- /* Unique-specific events */
+ /* Unique proxy-specific events */
+ /// Property of the type of RMRK collection could not be read successfully.
CorruptedCollectionType,
- NftTypeEncodeError,
+ // NftTypeEncodeError,
+ /// Too many symbols supplied as the property key. The maximum is [256](up_data_structs::MAX_PROPERTY_KEY_LENGTH).
RmrkPropertyKeyIsTooLong,
+ /// Too many bytes supplied as the property value. The maximum is [32768](up_data_structs::MAX_PROPERTY_VALUE_LENGTH).
RmrkPropertyValueIsTooLong,
+ /// Could not find a property by the supplied key.
RmrkPropertyIsNotFound,
+ /// Something went wrong when decoding encoded data from the storage.
+ /// Perhaps, there was a wrong key supplied for the type, or the data was improperly stored.
UnableToDecodeRmrkData,
/* RMRK compatible events */
+ /// Only destroying collections without tokens is allowed.
CollectionNotEmpty,
+ /// Could not find an ID for a collection. It is likely there were too many collections created on the chain.
NoAvailableCollectionId,
+ /// Token does not exist, or there is no suitable ID for it, likely too many tokens were created in a collection.
NoAvailableNftId,
+ /// Collection does not exist, has a wrong type, or does not map to a Unique ID.
CollectionUnknown,
+ /// No permission to perform action.
NoPermission,
+ /// Token is marked as non-transferable, and thus cannot be transferred.
NonTransferable,
+ /// Too many tokens created in the collection, no new ones are allowed.
CollectionFullOrLocked,
+ /// No such resource found.
ResourceDoesntExist,
+ /// If an NFT is sent to a descendant, that would form a nesting loop, an ouroboros.
+ /// Sending to self is redundant.
CannotSendToDescendentOrSelf,
+ /// Not the target owner of the sent NFT.
CannotAcceptNonOwnedNft,
+ /// Not the target owner of the sent NFT.
CannotRejectNonOwnedNft,
+ /// NFT was not sent and is not pending.
CannotRejectNonPendingNft,
+ /// Resource is not pending for the operation.
ResourceNotPending,
+ /// Could not find an ID for the resource. Is is likely there were too many resources created on an NFT.
NoAvailableResourceId,
}
#[pallet::call]
impl<T: Config> Pallet<T> {
- /// Create a collection
+ // todo :refactor replace every collection_id with rmrk_collection_id (and nft_id) in arguments for uniformity?
+
+ /// Create a new collection of NFTs.
+ ///
+ /// # Permissions:
+ /// * Anyone - will be assigned as the issuer of the collection.
+ ///
+ /// # Arguments:
+ /// - `metadata`: Metadata describing the collection, e.g. IPFS hash. Cannot be changed.
+ /// - `max`: Optional maximum number of tokens.
+ /// - `symbol`: UTF-8 string with token prefix, by which to represent the token in wallets and UIs.
+ /// Analogous to Unique's [`token_prefix`](up_data_structs::Collection). Cannot be changed.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::create_collection())]
pub fn create_collection(
@@ -226,8 +358,8 @@
T::CrossAccountId::from_sub(sender.clone()),
data,
[
- Self::rmrk_property(Metadata, &metadata)?,
- Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,
+ Self::encode_rmrk_property(Metadata, &metadata)?,
+ Self::encode_rmrk_property(CollectionType, &misc::CollectionType::Regular)?,
]
.into_iter(),
)?;
@@ -237,8 +369,8 @@
<PalletCommon<T>>::set_scoped_collection_property(
unique_collection_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(RmrkInternalCollectionId, &rmrk_collection_id)?,
)?;
<CollectionIndex<T>>::mutate(|n| *n += 1);
@@ -251,7 +383,15 @@
Ok(())
}
- /// destroy collection
+ /// Destroy a collection.
+ ///
+ /// Only empty collections can be destroyed. If it has any tokens, they must be burned first.
+ ///
+ /// # Permissions:
+ /// * Collection issuer
+ ///
+ /// # Arguments:
+ /// - `collection_id`: RMRK ID of the collection to destroy.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::destroy_collection())]
pub fn destroy_collection(
@@ -278,12 +418,14 @@
Ok(())
}
- /// Change the issuer of a collection
+ /// Change the issuer of a collection. Analogous to Unique's collection's [`owner`](up_data_structs::Collection).
+ ///
+ /// # Permissions:
+ /// * Collection issuer
///
- /// Parameters:
- /// - `origin`: sender of the transaction
- /// - `collection_id`: collection id of the nft to change issuer of
- /// - `new_issuer`: Collection's new issuer
+ /// # Arguments:
+ /// - `collection_id`: RMRK collection ID to change the issuer of.
+ /// - `new_issuer`: Collection's new issuer.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::change_collection_issuer())]
pub fn change_collection_issuer(
@@ -314,7 +456,13 @@
Ok(())
}
- /// lock collection
+ /// "Lock" the collection and prevent new token creation. Cannot be undone.
+ ///
+ /// # Permissions:
+ /// * Collection issuer
+ ///
+ /// # Arguments:
+ /// - `collection_id`: RMRK ID of the collection to lock.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::lock_collection())]
pub fn lock_collection(
@@ -346,16 +494,19 @@
Ok(())
}
- /// Mints an NFT in the specified collection
- /// Sets metadata and the royalty attribute
+ /// Mint an NFT in a specified collection.
///
- /// Parameters:
- /// - `collection_id`: The class of the asset to be minted.
- /// - `nft_id`: The nft value of the asset to be minted.
- /// - `recipient`: Receiver of the royalty
- /// - `royalty`: Permillage reward from each trade for the Recipient
- /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash
- /// - `transferable`: Ability to transfer this NFT
+ /// # Permissions:
+ /// * Collection issuer
+ ///
+ /// # Arguments:
+ /// - `owner`: Owner account of the NFT. If set to None, defaults to the sender (collection issuer).
+ /// - `collection_id`: RMRK collection ID for the NFT to be minted within. Cannot be changed.
+ /// - `recipient`: Receiver account of the royalty. Has no effect if the `royalty_amount` is not set. Cannot be changed.
+ /// - `royalty_amount`: Optional permillage reward from each trade for the `recipient`. Cannot be changed.
+ /// - `metadata`: Arbitrary data about an NFT, e.g. IPFS hash. Cannot be changed.
+ /// - `transferable`: Can this NFT be transferred? Cannot be changed.
+ /// - `resources`: Resource data to be added to the NFT immediately after minting.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::mint_nft(resources.as_ref().map(|r| r.len() as u32).unwrap_or(0)))]
pub fn mint_nft(
@@ -390,16 +541,16 @@
&cross_owner,
&collection,
[
- Self::rmrk_property(TokenType, &NftType::Regular)?,
- Self::rmrk_property(Transferable, &transferable)?,
- Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
- Self::rmrk_property(RoyaltyInfo, &royalty_info)?,
- Self::rmrk_property(Metadata, &metadata)?,
- Self::rmrk_property(Equipped, &false)?,
- Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
- Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
- Self::rmrk_property(PendingChildren, &PendingChildrenSet::new())?,
- Self::rmrk_property(AssociatedBases, &BasesMap::new())?,
+ Self::encode_rmrk_property(TokenType, &NftType::Regular)?,
+ Self::encode_rmrk_property(Transferable, &transferable)?,
+ Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
+ Self::encode_rmrk_property(RoyaltyInfo, &royalty_info)?,
+ Self::encode_rmrk_property(Metadata, &metadata)?,
+ Self::encode_rmrk_property(Equipped, &false)?,
+ Self::encode_rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,
+ Self::encode_rmrk_property(NextResourceId, &(0 as RmrkResourceId))?,
+ Self::encode_rmrk_property(PendingChildren, &PendingChildrenSet::new())?,
+ Self::encode_rmrk_property(AssociatedBases, &BasesMap::new())?,
]
.into_iter(),
)
@@ -423,7 +574,21 @@
Ok(())
}
- /// burn nft
+ /// Burn an NFT, destroying it and its nested tokens up to the specified limit.
+ /// If the burning budget is exceeded, the transaction is reverted.
+ ///
+ /// This is the way to burn a nested token as well.
+ ///
+ /// For more information, see [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively).
+ ///
+ /// # Permissions:
+ /// * Token owner
+ ///
+ /// # Arguments:
+ /// - `collection_id`: RMRK ID of the collection in which the NFT to burn belongs to.
+ /// - `nft_id`: ID of the NFT to be destroyed.
+ /// - `max_burns`: Maximum number of tokens to burn, used for nesting. The transaction
+ /// is reverted if there are more tokens to burn in the nesting tree than this number.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::burn_nft(*max_burns))]
pub fn burn_nft(
@@ -458,13 +623,19 @@
Ok(())
}
- /// Transfers a NFT from an Account or NFT A to another Account or NFT B
+ /// Transfer an NFT from an account/NFT A to another account/NFT B.
+ /// The token must be transferable. Nesting cannot occur deeper than the [`NESTING_BUDGET`].
+ ///
+ /// If the target owner is an NFT owned by another account, then the NFT will enter
+ /// the pending state and will have to be accepted by the other account.
///
- /// Parameters:
- /// - `origin`: sender of the transaction
- /// - `rmrk_collection_id`: collection id of the nft to be transferred
- /// - `rmrk_nft_id`: nft id of the nft to be transferred
- /// - `new_owner`: new owner of the nft which can be either an account or a NFT
+ /// # Permissions:
+ /// - Token owner
+ ///
+ /// # Arguments:
+ /// - `collection_id`: RMRK ID of the collection of the NFT to be transferred.
+ /// - `nft_id`: ID of the NFT to be transferred.
+ /// - `new_owner`: New owner of the nft which can be either an account or a NFT.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::send())]
pub fn send(
@@ -535,8 +706,8 @@
<PalletNft<T>>::set_scoped_token_property(
collection.id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property::<Option<PendingTarget>>(
+ RMRK_SCOPE,
+ Self::encode_rmrk_property::<Option<PendingTarget>>(
PendingNftAccept,
&Some((target_collection_id, target_nft_id.into())),
)?,
@@ -578,14 +749,18 @@
Ok(())
}
- /// Accepts an NFT sent from another account to self or owned NFT
+ /// Accept an NFT sent from another account to self or an owned NFT.
+ ///
+ /// The NFT in question must be pending, and, thus, be [sent](`crate::pallet::Call::send`) first.
+ ///
+ /// # Permissions:
+ /// - Token-owner-to-be
///
- /// Parameters:
- /// - `origin`: sender of the transaction
- /// - `rmrk_collection_id`: collection id of the nft to be accepted
- /// - `rmrk_nft_id`: nft id of the nft to be accepted
- /// - `new_owner`: either origin's account ID or origin-owned NFT, whichever the NFT was
- /// sent to
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT to be accepted.
+ /// - `rmrk_nft_id`: ID of the NFT to be accepted.
+ /// - `new_owner`: Either the sender's account ID or a sender-owned NFT,
+ /// whichever the accepted NFT was sent to.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::accept_nft())]
pub fn accept_nft(
@@ -650,8 +825,8 @@
<PalletNft<T>>::set_scoped_token_property(
collection.id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(PendingNftAccept, &None::<PendingTarget>)?,
)?;
}
@@ -665,12 +840,17 @@
Ok(())
}
- /// Rejects an NFT sent from another account to self or owned NFT
- ///
- /// Parameters:
- /// - `origin`: sender of the transaction
- /// - `rmrk_collection_id`: collection id of the nft to be accepted
- /// - `rmrk_nft_id`: nft id of the nft to be accepted
+ /// Reject an NFT sent from another account to self or owned NFT.
+ /// The NFT in question will not be sent back and burnt instead.
+ ///
+ /// The NFT in question must be pending, and, thus, be [sent](`crate::pallet::Call::send`) first.
+ ///
+ /// # Permissions:
+ /// - Token-owner-to-be-not
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK ID of the NFT to be rejected.
+ /// - `rmrk_nft_id`: ID of the NFT to be rejected.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::reject_nft())]
pub fn reject_nft(
@@ -724,7 +904,19 @@
Ok(())
}
- /// accept the addition of a new resource to an existing NFT
+ /// Accept the addition of a newly created pending resource to an existing NFT.
+ ///
+ /// This transaction is needed when a resource is created and assigned to an NFT
+ /// by a non-owner, i.e. the collection issuer, with one of the
+ /// [`add_...` transactions](crate::pallet::Call::add_basic_resource).
+ ///
+ /// # Permissions:
+ /// - Token owner
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `rmrk_nft_id`: ID of the NFT with a pending resource to be accepted.
+ /// - `resource_id`: ID of the newly created pending resource.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::accept_resource())]
pub fn accept_resource(
@@ -767,7 +959,18 @@
Ok(())
}
- /// accept the removal of a resource of an existing NFT
+ /// Accept the removal of a removal-pending resource from an NFT.
+ ///
+ /// This transaction is needed when a non-owner, i.e. the collection issuer,
+ /// requests a [removal](`crate::pallet::Call::remove_resource`) of a resource from an NFT.
+ ///
+ /// # Permissions:
+ /// - Token owner
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `rmrk_nft_id`: ID of the NFT with a resource to be removed.
+ /// - `resource_id`: ID of the removal-pending resource.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::accept_resource_removal())]
pub fn accept_resource_removal(
@@ -795,17 +998,17 @@
ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);
- let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;
+ let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
let resource_info = <PalletNft<T>>::token_aux_property((
collection_id,
nft_id,
- PropertyScope::Rmrk,
+ RMRK_SCOPE,
resource_id_key.clone(),
))
.ok_or(<Error<T>>::ResourceDoesntExist)?;
- let resource_info: RmrkResourceInfo = Self::decode_property(&resource_info)?;
+ let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource_info)?;
ensure!(
resource_info.pending_removal,
@@ -815,7 +1018,7 @@
<PalletNft<T>>::remove_token_aux_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
+ RMRK_SCOPE,
resource_id_key,
);
@@ -833,7 +1036,22 @@
Ok(())
}
- /// set a custom value on an NFT
+ /// Add or edit a custom user property, a key-value pair, describing the metadata
+ /// of a token or a collection, on either one of these.
+ ///
+ /// Note that in this proxy implementation many details regarding RMRK are stored
+ /// as scoped properties prefixed with "rmrk:", normally inaccessible
+ /// to external transactions and RPCs.
+ ///
+ /// # Permissions:
+ /// - Collection issuer - in case of collection property
+ /// - Token owner - in case of NFT property
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID.
+ /// - `maybe_nft_id`: Optional ID of the NFT. If left empty, then the property is set for the collection.
+ /// - `key`: Key of the custom property to be referenced by.
+ /// - `value`: Value of the custom property to be stored.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::set_property())]
pub fn set_property(
@@ -863,8 +1081,8 @@
<PalletNft<T>>::set_scoped_token_property(
collection_id,
token_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(UserProperty(key.as_slice()), &value)?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
)?;
}
None => {
@@ -877,8 +1095,8 @@
<PalletCommon<T>>::set_scoped_collection_property(
collection_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(UserProperty(key.as_slice()), &value)?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(UserProperty(key.as_slice()), &value)?,
)?;
}
}
@@ -893,7 +1111,20 @@
Ok(())
}
- /// set a different order of resource priority
+ /// Set a different order of resource priorities for an NFT. Priorities can be used,
+ /// for example, for order of rendering.
+ ///
+ /// Note that the priorities are not updated automatically, and are an empty vector
+ /// by default. There is no pre-set definition for the order to be particular,
+ /// it can be interpreted arbitrarily use-case by use-case.
+ ///
+ /// # Permissions:
+ /// - Token owner
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `rmrk_nft_id`: ID of the NFT to rearrange resource priorities for.
+ /// - `priorities`: Ordered vector of resource IDs.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::set_priority())]
pub fn set_priority(
@@ -920,8 +1151,8 @@
<PalletNft<T>>::set_scoped_token_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(ResourcePriorities, &priorities.into_inner())?,
)?;
Self::deposit_event(Event::<T>::PrioritySet {
@@ -932,7 +1163,23 @@
Ok(())
}
- /// Create basic resource
+ /// Create and set/propose a basic resource for an NFT.
+ ///
+ /// A resource is considered a part of an NFT, an additional piece of metadata
+ /// usually serving to add a piece of media on top of the root metadata, be it
+ /// a different wing on the root template bird or something entirely unrelated.
+ /// A basic resource is the simplest, lacking a base or composables.
+ ///
+ /// See RMRK docs for more information and examples.
+ ///
+ /// # Permissions:
+ /// - Collection issuer - if not the token owner, adding the resource will warrant
+ /// the owner's [acceptance](crate::pallet::Call::accept_resource).
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `nft_id`: ID of the NFT to assign a resource to.
+ /// - `resource`: Data of the resource to be created.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_basic_resource())]
pub fn add_basic_resource(
@@ -962,7 +1209,23 @@
Ok(())
}
- /// Create composable resource
+ /// Create and set/propose a composable resource for an NFT.
+ ///
+ /// A resource is considered a part of an NFT, an additional piece of metadata
+ /// usually serving to add a piece of media on top of the root metadata, be it
+ /// a different wing on the root template bird or something entirely unrelated.
+ /// A composable resource links to a base and has a subset of its parts it is composed of.
+ ///
+ /// See RMRK docs for more information and examples.
+ ///
+ /// # Permissions:
+ /// - Collection issuer - if not the token owner, adding the resource will warrant
+ /// the owner's [acceptance](crate::pallet::Call::accept_resource).
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `nft_id`: ID of the NFT to assign a resource to.
+ /// - `resource`: Data of the resource to be created.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_composable_resource())]
pub fn add_composable_resource(
@@ -990,17 +1253,17 @@
<PalletNft<T>>::try_mutate_token_aux_property(
collection_id,
nft_id.into(),
- PropertyScope::Rmrk,
- Self::rmrk_property_key(AssociatedBases)?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(AssociatedBases)?,
|value| -> DispatchResult {
let mut bases: BasesMap = match value {
- Some(value) => Self::decode_property(value)?,
+ Some(value) => Self::decode_property_value(value)?,
None => BasesMap::new(),
};
*bases.entry(base_id).or_insert(0) += 1;
- *value = Some(Self::encode_property(&bases)?);
+ *value = Some(Self::encode_property_value(&bases)?);
Ok(())
},
)?;
@@ -1012,7 +1275,23 @@
Ok(())
}
- /// Create slot resource
+ /// Create and set/propose a slot resource for an NFT.
+ ///
+ /// A resource is considered a part of an NFT, an additional piece of metadata
+ /// usually serving to add a piece of media on top of the root metadata, be it
+ /// a different wing on the root template bird or something entirely unrelated.
+ /// A slot resource links to a base and a slot in it which it now occupies.
+ ///
+ /// See RMRK docs for more information and examples.
+ ///
+ /// # Permissions:
+ /// - Collection issuer - if not the token owner, adding the resource will warrant
+ /// the owner's [acceptance](crate::pallet::Call::accept_resource).
+ ///
+ /// # Arguments:
+ /// - `rmrk_collection_id`: RMRK collection ID of the NFT.
+ /// - `nft_id`: ID of the NFT to assign a resource to.
+ /// - `resource`: Data of the resource to be created.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::add_slot_resource())]
pub fn add_slot_resource(
@@ -1042,7 +1321,18 @@
Ok(())
}
- /// remove resource
+ /// Remove and erase a resource from an NFT.
+ ///
+ /// If the sender does not own the NFT, then it will be pending confirmation,
+ /// and will have to be [accepted](crate::pallet::Call::accept_resource_removal) by the token owner.
+ ///
+ /// # Permissions
+ /// - Collection issuer
+ ///
+ /// # Arguments
+ /// - `collection_id`: RMRK ID of a collection to which the NFT making use of the resource belongs to.
+ /// - `nft_id`: ID of the NFT with a resource to be removed.
+ /// - `resource_id`: ID of the resource to be removed.
#[transactional]
#[pallet::weight(<SelfWeightOf<T>>::remove_resource())]
pub fn remove_resource(
@@ -1070,31 +1360,34 @@
}
impl<T: Config> Pallet<T> {
- pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {
+ /// Transform one of possible RMRK keys into a byte key with a RMRK scope.
+ pub fn get_scoped_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {
let key = rmrk_key.to_key::<T>()?;
- let scoped_key = PropertyScope::Rmrk
+ let scoped_key = RMRK_SCOPE
.apply(key)
.map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;
Ok(scoped_key)
}
- // todo think about renaming these
- pub fn rmrk_property<E: Encode>(
+ /// Form a Unique property, transforming a RMRK key into bytes (without assigning the scope yet)
+ /// and encoding the value from an arbitrary type into bytes.
+ pub fn encode_rmrk_property<E: Encode>(
rmrk_key: RmrkProperty,
value: &E,
) -> Result<Property, DispatchError> {
let key = rmrk_key.to_key::<T>()?;
- let value = Self::encode_property(value)?;
+ let value = Self::encode_property_value(value)?;
let property = Property { key, value };
Ok(property)
}
- pub fn encode_property<E: Encode, S: Get<u32>>(
+ /// Encode property value from an arbitrary type into bytes for storage.
+ pub fn encode_property_value<E: Encode, S: Get<u32>>(
value: &E,
) -> Result<BoundedBytes<S>, DispatchError> {
let value = value
@@ -1105,13 +1398,15 @@
Ok(value)
}
- pub fn decode_property<D: Decode, S: Get<u32>>(
+ /// Decode property value from bytes into an arbitrary type.
+ pub fn decode_property_value<D: Decode, S: Get<u32>>(
vec: &BoundedBytes<S>,
) -> Result<D, DispatchError> {
vec.decode()
.map_err(|_| <Error<T>>::UnableToDecodeRmrkData.into())
}
+ /// Change the limit of a property value byte vector.
pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>
where
BoundedVec<u8, S>: TryFrom<Vec<u8>>,
@@ -1120,6 +1415,9 @@
.map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())
}
+ /// Initialize a new NFT collection with certain RMRK-scoped properties.
+ ///
+ /// See [`init_collection`](pallet_nonfungible::pallet::Pallet::init_collection) for more details.
fn init_collection(
sender: T::CrossAccountId,
data: CreateCollectionData<T::AccountId>,
@@ -1133,13 +1431,16 @@
<PalletCommon<T>>::set_scoped_collection_properties(
collection_id?,
- PropertyScope::Rmrk,
+ RMRK_SCOPE,
properties,
)?;
collection_id
}
+ /// Mint a new NFT with certain RMRK-scoped properties. Sender must be the collection owner.
+ ///
+ /// See [`create_item`](pallet_nonfungible::pallet::Pallet::create_item) for more details.
pub fn create_nft(
sender: &T::CrossAccountId,
owner: &T::CrossAccountId,
@@ -1160,13 +1461,16 @@
<PalletNft<T>>::set_scoped_token_properties(
collection.id,
nft_id,
- PropertyScope::Rmrk,
+ RMRK_SCOPE,
properties,
)?;
Ok(nft_id)
}
+ /// Burn an NFT, along with its nested children, limited by `max_burns`. The sender must be the token owner.
+ ///
+ /// See [`burn_recursively`](pallet_nonfungible::pallet::Pallet::burn_recursively) for more details.
fn destroy_nft(
sender: T::CrossAccountId,
collection_id: CollectionId,
@@ -1207,48 +1511,54 @@
)
}
+ /// Add a sent token pending acceptance to the target owning token as a property.
fn insert_pending_child(
target: (CollectionId, TokenId),
child: (RmrkCollectionId, RmrkNftId),
) -> DispatchResult {
- Self::mutate_pending_child(target, |pending_children| {
+ Self::mutate_pending_children(target, |pending_children| {
pending_children.insert(child);
})
}
+ /// Remove a sent token pending acceptance from the target token's properties.
fn remove_pending_child(
target: (CollectionId, TokenId),
child: (RmrkCollectionId, RmrkNftId),
) -> DispatchResult {
- Self::mutate_pending_child(target, |pending_children| {
+ Self::mutate_pending_children(target, |pending_children| {
pending_children.remove(&child);
})
}
- fn mutate_pending_child(
+ /// Apply a mutation to the property of a token containing sent tokens
+ /// that are currently pending acceptance.
+ fn mutate_pending_children(
(target_collection_id, target_nft_id): (CollectionId, TokenId),
f: impl FnOnce(&mut PendingChildrenSet),
) -> DispatchResult {
<PalletNft<T>>::try_mutate_token_aux_property(
target_collection_id,
target_nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(PendingChildren)?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(PendingChildren)?,
|pending_children| -> DispatchResult {
let mut map = match pending_children {
- Some(map) => Self::decode_property(map)?,
+ Some(map) => Self::decode_property_value(map)?,
None => PendingChildrenSet::new(),
};
f(&mut map);
- *pending_children = Some(Self::encode_property(&map)?);
+ *pending_children = Some(Self::encode_property_value(&map)?);
Ok(())
},
)
}
+ /// Get an iterator from a token's property containing tokens sent to it
+ /// that are currently pending acceptance.
fn iterate_pending_children(
collection_id: CollectionId,
nft_id: TokenId,
@@ -1256,18 +1566,20 @@
let property = <PalletNft<T>>::token_aux_property((
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(PendingChildren)?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(PendingChildren)?,
));
let pending_children = match property {
- Some(map) => Self::decode_property(&map)?,
+ Some(map) => Self::decode_property_value(&map)?,
None => PendingChildrenSet::new(),
};
Ok(pending_children.into_iter())
}
+ /// Get incremented resource ID from within an NFT's properties and store the new latest ID.
+ /// Thus, the returned resource ID should be used.
fn acquire_next_resource_id(
collection_id: CollectionId,
nft_id: TokenId,
@@ -1282,13 +1594,15 @@
<PalletNft<T>>::set_scoped_token_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property(NextResourceId, &next_id)?,
+ RMRK_SCOPE,
+ Self::encode_rmrk_property(NextResourceId, &next_id)?,
)?;
Ok(resource_id)
}
+ /// Create and add a resource for a regular NFT, mark it as pending if the sender
+ /// is not the token owner. The sender must be the collection owner.
fn resource_add(
sender: T::AccountId,
collection_id: CollectionId,
@@ -1319,10 +1633,10 @@
<PalletNft<T>>::try_mutate_token_aux_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(ResourceId(id))?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(ResourceId(id))?,
|value| -> DispatchResult {
- *value = Some(Self::encode_property(&resource_info)?);
+ *value = Some(Self::encode_property_value(&resource_info)?);
Ok(())
},
@@ -1331,6 +1645,8 @@
Ok(id)
}
+ /// Designate a resource for erasure from an NFT, and remove it if the sender is the token owner.
+ /// The sender must be the collection owner.
fn resource_remove(
sender: T::AccountId,
collection_id: CollectionId,
@@ -1341,18 +1657,17 @@
Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;
ensure!(collection.owner == sender, Error::<T>::NoPermission);
- let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?;
- let scope = PropertyScope::Rmrk;
+ let resource_id_key = Self::get_scoped_property_key(ResourceId(resource_id))?;
let resource = <PalletNft<T>>::token_aux_property((
collection_id,
nft_id,
- scope,
+ RMRK_SCOPE,
resource_id_key.clone(),
))
.ok_or(<Error<T>>::ResourceDoesntExist)?;
- let resource_info: RmrkResourceInfo = Self::decode_property(&resource)?;
+ let resource_info: RmrkResourceInfo = Self::decode_property_value(&resource)?;
let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
let topmost_owner =
@@ -1363,8 +1678,8 @@
<PalletNft<T>>::remove_token_aux_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(ResourceId(resource_id))?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(ResourceId(resource_id))?,
);
if let RmrkResourceTypes::Composable(resource) = resource_info.resource {
@@ -1383,6 +1698,8 @@
Ok(())
}
+ /// Remove one usage of a base from an NFT's property of associated bases. The base will stay, however,
+ /// if the count of resources using the base is still non-zero.
fn remove_associated_base_id(
collection_id: CollectionId,
nft_id: TokenId,
@@ -1391,11 +1708,11 @@
<PalletNft<T>>::try_mutate_token_aux_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(AssociatedBases)?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(AssociatedBases)?,
|value| -> DispatchResult {
let mut bases: BasesMap = match value {
- Some(value) => Self::decode_property(value)?,
+ Some(value) => Self::decode_property_value(value)?,
None => BasesMap::new(),
};
@@ -1407,12 +1724,13 @@
}
}
- *value = Some(Self::encode_property(&bases)?);
+ *value = Some(Self::encode_property_value(&bases)?);
Ok(())
},
)
}
+ /// Apply a mutation to a resource stored in the token properties of an NFT.
fn try_mutate_resource_info(
collection_id: CollectionId,
nft_id: TokenId,
@@ -1422,15 +1740,15 @@
<PalletNft<T>>::try_mutate_token_aux_property(
collection_id,
nft_id,
- PropertyScope::Rmrk,
- Self::rmrk_property_key(ResourceId(resource_id))?,
+ RMRK_SCOPE,
+ Self::get_scoped_property_key(ResourceId(resource_id))?,
|value| match value {
Some(value) => {
- let mut resource_info: RmrkResourceInfo = Self::decode_property(value)?;
+ let mut resource_info: RmrkResourceInfo = Self::decode_property_value(value)?;
f(&mut resource_info)?;
- *value = Self::encode_property(&resource_info)?;
+ *value = Self::encode_property_value(&resource_info)?;
Ok(())
}
@@ -1439,6 +1757,7 @@
)
}
+ /// Change the owner of an NFT collection, ensuring that the sender is the current owner.
fn change_collection_owner(
collection_id: CollectionId,
collection_type: misc::CollectionType,
@@ -1454,6 +1773,7 @@
collection.save()
}
+ /// Ensure that an account is the collection owner/issuer, return an error if not.
pub fn check_collection_owner(
collection: &NonfungibleHandle<T>,
account: &T::CrossAccountId,
@@ -1463,10 +1783,12 @@
.map_err(Self::map_unique_err_to_proxy)
}
+ /// Get the latest yet-unused RMRK collection index from the storage.
pub fn last_collection_idx() -> RmrkCollectionId {
<CollectionIndex<T>>::get()
}
+ /// Get a mapping from a RMRK collection ID to its corresponding Unique collection ID.
pub fn unique_collection_id(
rmrk_collection_id: RmrkCollectionId,
) -> Result<CollectionId, DispatchError> {
@@ -1474,12 +1796,14 @@
.map_err(|_| <Error<T>>::CollectionUnknown.into())
}
+ /// Get a mapping from a Unique collection ID to its RMRK collection ID counterpart, if it exists.
pub fn rmrk_collection_id(
unique_collection_id: CollectionId,
) -> Result<RmrkCollectionId, DispatchError> {
Self::get_collection_property_decoded(unique_collection_id, RmrkInternalCollectionId)
}
+ /// Fetch a Unique NFT collection.
pub fn get_nft_collection(
collection_id: CollectionId,
) -> Result<NonfungibleHandle<T>, DispatchError> {
@@ -1492,29 +1816,35 @@
}
}
+ /// Check if an NFT collection with such an ID exists.
pub fn collection_exists(collection_id: CollectionId) -> bool {
<CollectionHandle<T>>::try_get(collection_id).is_ok()
}
+ /// Fetch and decode a RMRK-scoped collection property value in bytes.
pub fn get_collection_property(
collection_id: CollectionId,
key: RmrkProperty,
) -> Result<PropertyValue, DispatchError> {
let collection_property = <PalletCommon<T>>::collection_properties(collection_id)
- .get(&Self::rmrk_property_key(key)?)
+ .get(&Self::get_scoped_property_key(key)?)
.ok_or(<Error<T>>::CollectionUnknown)?
.clone();
Ok(collection_property)
}
+ /// Fetch a RMRK-scoped collection property and decode it from bytes into an appropriate type.
pub fn get_collection_property_decoded<V: Decode>(
collection_id: CollectionId,
key: RmrkProperty,
) -> Result<V, DispatchError> {
- Self::decode_property(&Self::get_collection_property(collection_id, key)?)
+ Self::decode_property_value(&Self::get_collection_property(collection_id, key)?)
}
+ /// Get the type of a collection stored in it as a scoped property.
+ ///
+ /// RMRK Core proxy differentiates between regular collections as well as RMRK bases as collections.
pub fn get_collection_type(
collection_id: CollectionId,
) -> Result<misc::CollectionType, DispatchError> {
@@ -1527,6 +1857,8 @@
})
}
+ /// Ensure that the type of the collection equals the provided type,
+ /// otherwise return an error.
pub fn ensure_collection_type(
collection_id: CollectionId,
collection_type: misc::CollectionType,
@@ -1540,6 +1872,7 @@
Ok(())
}
+ /// Fetch an NFT collection, but make sure it has the appropriate type.
pub fn get_typed_nft_collection(
collection_id: CollectionId,
collection_type: misc::CollectionType,
@@ -1549,6 +1882,8 @@
Self::get_nft_collection(collection_id)
}
+ /// Same as [`get_typed_nft_collection`](crate::pallet::Pallet::get_typed_nft_collection),
+ /// but also return the Unique collection ID.
pub fn get_typed_nft_collection_mapped(
rmrk_collection_id: RmrkCollectionId,
collection_type: misc::CollectionType,
@@ -1563,31 +1898,37 @@
Ok((collection, unique_collection_id))
}
+ /// Fetch and decode a RMRK-scoped NFT property value in bytes.
pub fn get_nft_property(
collection_id: CollectionId,
nft_id: TokenId,
key: RmrkProperty,
) -> Result<PropertyValue, DispatchError> {
let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))
- .get(&Self::rmrk_property_key(key)?)
+ .get(&Self::get_scoped_property_key(key)?)
.ok_or(<Error<T>>::RmrkPropertyIsNotFound)?
.clone();
Ok(nft_property)
}
+ /// Fetch a RMRK-scoped NFT property and decode it from bytes into an appropriate type.
pub fn get_nft_property_decoded<V: Decode>(
collection_id: CollectionId,
nft_id: TokenId,
key: RmrkProperty,
) -> Result<V, DispatchError> {
- Self::decode_property(&Self::get_nft_property(collection_id, nft_id, key)?)
+ Self::decode_property_value(&Self::get_nft_property(collection_id, nft_id, key)?)
}
+ /// Check that an NFT exists.
pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {
<TokenData<T>>::contains_key((collection_id, nft_id))
}
+ /// Get the type of an NFT stored in it as a scoped property.
+ ///
+ /// RMRK Core proxy differentiates between regular NFTs, and RMRK parts and themes.
pub fn get_nft_type(
collection_id: CollectionId,
token_id: TokenId,
@@ -1596,6 +1937,7 @@
.map_err(|_| <Error<T>>::NoAvailableNftId.into())
}
+ /// Ensure that the type of the NFT equals the provided type, otherwise return an error.
pub fn ensure_nft_type(
collection_id: CollectionId,
token_id: TokenId,
@@ -1607,6 +1949,8 @@
Ok(())
}
+ /// Ensure that an account is the owner of the token, either directly
+ /// or at the top of the nesting hierarchy; return an error if it is not.
pub fn ensure_nft_owner(
collection_id: CollectionId,
token_id: TokenId,
@@ -1627,6 +1971,8 @@
Ok(())
}
+ /// Fetch non-scoped properties of a collection or a token that match the filter keys supplied,
+ /// or, if None are provided, return all non-scoped properties.
pub fn filter_user_properties<Key, Value, R, Mapper>(
collection_id: CollectionId,
token_id: Option<TokenId>,
@@ -1672,6 +2018,8 @@
})
}
+ /// Get all non-scoped properties from a collection or a token, and apply some transformation
+ /// to each key-value pair.
pub fn iterate_user_properties<Key, Value, R, Mapper>(
collection_id: CollectionId,
token_id: Option<TokenId>,
@@ -1699,6 +2047,7 @@
Ok(properties)
}
+ /// Match Unique errors to RMRK's own and return the RMRK error if a match is successful.
fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {
map_unique_err_to_proxy! {
match err {
pallets/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 errors from one type to another and return an error
+/// 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,9 @@
};
}
-// Utilize the RmrkCore pallet for access to Runtime errors.
+/// Interface to decode bytes from a bounded vector into an arbitrary type.
pub trait RmrkDecode<T: Decode, S> {
+ /// Try to decode bytes from a bounded vector into an arbitrary type.
fn decode(&self) -> Result<T, Error>;
}
@@ -43,8 +48,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 +64,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,
pallets/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()
}
pallets/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,8 @@
}))
}
+
+/// Get tokens owned by an account in a collection.
pub fn account_tokens<T: Config>(
account_id: T::AccountId,
collection_id: RmrkCollectionId,
@@ -116,6 +139,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 +176,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 +199,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 +225,7 @@
Ok(properties)
}
+/// Get data of resources of an NFT.
pub fn nft_resources<T: Config>(
collection_id: RmrkCollectionId,
nft_id: RmrkNftId,
@@ -226,7 +253,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 +262,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,
pallets/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};
pallets/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,88 @@
// 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.
+//!
+//! 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>
+//!
+//! ## 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 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 proeprties, which, along with other safeguards,
+//! makes them impossible to tamper with.
+//!
+//! ### Collection and NFT Types
+//!
+//! RMRK introduces the concept of a Base, which is a catalgoue 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 the collection's NFTs. See [`CollectionType`](pallet_rmrk_core::misc::CollectionType) and
+//! [`NftType`](pallet_rmrk_core::misc::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 +127,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 +165,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.
NoAvailableBaseId,
+ /// Could not find a suitable ID for a part, likely too many part tokens were created in the base.
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.
+ ///
+ /// 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.
///
- /// 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
+ /// # 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 +228,8 @@
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 +248,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.
+ ///
+ /// Modeled after [themeadd interaction](https://github.com/rmrk-team/rmrk-spec/blob/master/standards/rmrk2.0.0/interactions/themeadd.md).
///
- /// 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
+ /// # 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 +290,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 +303,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 +313,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 +363,7 @@
base_collection_id,
part_id,
PropertyScope::Rmrk,
- <PalletCore<T>>::rmrk_property(EquippableList, &equippables)?,
+ <PalletCore<T>>::encode_rmrk_property(EquippableList, &equippables)?,
)?;
}
}
@@ -266,6 +376,8 @@
}
impl<T: Config> Pallet<T> {
+ /// Create or renew an NFT serving as a part, setting its properties
+ /// to those of the part.
fn create_part(
sender: &T::CrossAccountId,
collection: &NonfungibleHandle<T>,
@@ -298,7 +410,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 +422,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 +434,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)
pallets/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,