difftreelog
Merge branch 'develop' into feature/CORE-386_1
in: master
54 files changed
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -93,5 +93,9 @@
bench-structure:
make _bench PALLET=structure
+.PHONY: bench-rmrk-core
+bench-rmrk-core:
+ make _bench PALLET=proxy-rmrk-core
+
.PHONY: bench
-bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible
+bench: bench-evm-migration bench-unique bench-structure bench-fungible bench-refungible bench-nonfungible bench-rmrk-core
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/common/src/benchmarking.rs
+++ b/pallets/common/src/benchmarking.rs
@@ -109,7 +109,7 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- |owner, data| <Pallet<T>>::init_collection(owner, data),
+ |owner, data| <Pallet<T>>::init_collection(owner, data, true),
|h| h,
)
}
pallets/common/src/dispatch.rsdiffbeforeafterboth--- a/pallets/common/src/dispatch.rs
+++ b/pallets/common/src/dispatch.rs
@@ -11,7 +11,7 @@
use crate::{pallet::Config, CommonCollectionOperations, CollectionHandle};
// TODO: move to benchmarking
-/// Price of [`dispatch_call`] call with noop `call` argument
+/// Price of [`dispatch_tx`] call with noop `call` argument
pub fn dispatch_weight<T: Config>() -> Weight {
// Read collection
<T as frame_system::Config>::DbWeight::get().reads(1)
@@ -21,7 +21,7 @@
}
/// Helper function to implement substrate calls for common collection methods
-pub fn dispatch_call<
+pub fn dispatch_tx<
T: Config,
C: FnOnce(&dyn CommonCollectionOperations<T>) -> DispatchResultWithPostInfo,
>(
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -427,7 +427,7 @@
/// Target collection doesn't supports this operation
UnsupportedOperation,
- /// Not sufficient founds to perform action
+ /// Not sufficient funds to perform action
NotSufficientFounds,
/// Collection has nesting disabled
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -51,7 +51,7 @@
create_collection_raw(
owner,
CollectionMode::NFT,
- <Pallet<T>>::init_collection,
+ |owner, data| <Pallet<T>>::init_collection(owner, data, true),
NonfungibleHandle::cast,
)
}
@@ -99,7 +99,7 @@
sender: cross_from_sub(owner); burner: cross_sub;
};
let item = create_max_item(&collection, &sender, burner.clone())?;
- }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+ }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
burn_recursively_breadth_plus_self_plus_self_per_each_raw {
let b in 0..200;
@@ -111,7 +111,7 @@
for i in 0..b {
create_max_item(&collection, &sender, T::CrossTokenAddressMapping::token_to_address(collection.id, item))?;
}
- }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)}
+ }: {<Pallet<T>>::burn_recursively(&collection, &burner, item, &Unlimited, &Unlimited)?}
transfer {
bench_init!{
@@ -183,7 +183,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props)?}
+ }: {<Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?}
delete_token_properties {
let b in 0..MAX_PROPERTIES_PER_ITEM;
@@ -205,7 +205,7 @@
value: property_value(),
}).collect::<Vec<_>>();
let item = create_max_item(&collection, &owner, owner.clone())?;
- <Pallet<T>>::set_token_properties(&collection, &owner, item, props)?;
+ <Pallet<T>>::set_token_properties(&collection, &owner, item, props, false)?;
let to_delete = (0..b).map(|k| property_key(k as usize)).collect::<Vec<_>>();
}: {<Pallet<T>>::delete_token_properties(&collection, &owner, item, to_delete)?}
}
pallets/proxy-rmrk-core/src/benchmarking.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/benchmarking.rs
@@ -0,0 +1,26 @@
+use sp_std::vec;
+
+use frame_benchmarking::{benchmarks, account};
+use frame_system::RawOrigin;
+use frame_support::{
+ traits::{Currency, Get},
+ BoundedVec,
+};
+
+use crate::{Config, Pallet, Call};
+
+const SEED: u32 = 1;
+
+fn create_data<S: Get<u32>>() -> BoundedVec<u8, S> {
+ vec![0; S::get() as usize].try_into().expect("size == S")
+}
+
+benchmarks! {
+ create_collection {
+ let caller = account("caller", 0, SEED);
+ <T as pallet_common::Config>::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+ let metadata = create_data();
+ // TODO: Fix CollectionTokenPrefixLimitExceeded with create_data
+ let symbol = vec![].try_into().expect("0 <= x");
+ }: _(RawOrigin::Signed(caller), metadata, None, symbol)
+}
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth1// 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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::{*, mapping::TokenAddressMapping};24use pallet_common::{25 Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::{Pallet as PalletStructure, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334pub mod misc;35pub mod property;3637use misc::*;38pub use property::*;3940use RmrkProperty::*;4142const NESTING_BUDGET: u32 = 5;4344#[frame_support::pallet]45pub mod pallet {46 use super::*;47 use pallet_evm::account;4849 #[pallet::config]50 pub trait Config:51 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config52 {53 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;54 }5556 #[pallet::storage]57 #[pallet::getter(fn collection_index)]58 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;5960 #[pallet::storage]61 pub type UniqueCollectionId<T: Config> =62 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;6364 #[pallet::storage]65 pub type RmrkInernalCollectionId<T: Config> =66 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;6768 #[pallet::pallet]69 #[pallet::generate_store(pub(super) trait Store)]70 pub struct Pallet<T>(_);7172 #[pallet::event]73 #[pallet::generate_deposit(pub(super) fn deposit_event)]74 pub enum Event<T: Config> {75 CollectionCreated {76 issuer: T::AccountId,77 collection_id: RmrkCollectionId,78 },79 CollectionDestroyed {80 issuer: T::AccountId,81 collection_id: RmrkCollectionId,82 },83 IssuerChanged {84 old_issuer: T::AccountId,85 new_issuer: T::AccountId,86 collection_id: RmrkCollectionId,87 },88 CollectionLocked {89 issuer: T::AccountId,90 collection_id: RmrkCollectionId,91 },92 NftMinted {93 owner: T::AccountId,94 collection_id: RmrkCollectionId,95 nft_id: RmrkNftId,96 },97 NFTBurned {98 owner: T::AccountId,99 nft_id: RmrkNftId,100 },101 NFTSent {102 sender: T::AccountId,103 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,104 collection_id: RmrkCollectionId,105 nft_id: RmrkNftId,106 approval_required: bool,107 },108 NFTAccepted {109 sender: T::AccountId,110 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111 collection_id: RmrkCollectionId,112 nft_id: RmrkNftId,113 },114 NFTRejected {115 sender: T::AccountId,116 collection_id: RmrkCollectionId,117 nft_id: RmrkNftId,118 },119 PropertySet {120 collection_id: RmrkCollectionId,121 maybe_nft_id: Option<RmrkNftId>,122 key: RmrkKeyString,123 value: RmrkValueString,124 },125 ResourceAdded {126 nft_id: RmrkNftId,127 resource_id: RmrkResourceId,128 },129 ResourceRemoval {130 nft_id: RmrkNftId,131 resource_id: RmrkResourceId,132 },133 ResourceAccepted {134 nft_id: RmrkNftId,135 resource_id: RmrkResourceId,136 },137 ResourceRemovalAccepted {138 nft_id: RmrkNftId,139 resource_id: RmrkResourceId,140 },141 PrioritySet {142 collection_id: RmrkCollectionId,143 nft_id: RmrkNftId,144 },145 }146147 #[pallet::error]148 pub enum Error<T> {149 /* Unique-specific events */150 CorruptedCollectionType,151 NftTypeEncodeError,152 RmrkPropertyKeyIsTooLong,153 RmrkPropertyValueIsTooLong,154155 /* RMRK compatible events */156 CollectionNotEmpty,157 NoAvailableCollectionId,158 NoAvailableNftId,159 CollectionUnknown,160 NoPermission,161 NonTransferable,162 CollectionFullOrLocked,163 ResourceDoesntExist,164 CannotSendToDescendentOrSelf,165 CannotAcceptNonOwnedNft,166 CannotRejectNonOwnedNft,167 ResourceNotPending,168 }169170 #[pallet::call]171 impl<T: Config> Pallet<T> {172 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]173 #[transactional]174 pub fn create_collection(175 origin: OriginFor<T>,176 metadata: RmrkString,177 max: Option<u32>,178 symbol: RmrkCollectionSymbol,179 ) -> DispatchResult {180 let sender = ensure_signed(origin)?;181182 let limits = CollectionLimits {183 owner_can_transfer: Some(false),184 token_limit: max,185 ..Default::default()186 };187188 let data = CreateCollectionData {189 limits: Some(limits),190 token_prefix: symbol191 .into_inner()192 .try_into()193 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,194 permissions: Some(CollectionPermissions {195 nesting: Some(NestingRule::Owner),196 ..Default::default()197 }),198 ..Default::default()199 };200201 let unique_collection_id = Self::init_collection(202 T::CrossAccountId::from_sub(sender.clone()),203 data,204 [205 Self::rmrk_property(Metadata, &metadata)?,206 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,207 ]208 .into_iter(),209 )?;210 let rmrk_collection_id = <CollectionIndex<T>>::get();211212 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);213 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);214215 <CollectionIndex<T>>::mutate(|n| *n += 1);216217 Self::deposit_event(Event::CollectionCreated {218 issuer: sender,219 collection_id: rmrk_collection_id,220 });221222 Ok(())223 }224225 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]226 #[transactional]227 pub fn destroy_collection(228 origin: OriginFor<T>,229 collection_id: RmrkCollectionId,230 ) -> DispatchResult {231 let sender = ensure_signed(origin)?;232 let cross_sender = T::CrossAccountId::from_sub(sender.clone());233234 let collection = Self::get_typed_nft_collection(235 Self::unique_collection_id(collection_id)?,236 misc::CollectionType::Regular,237 )?;238 collection.check_is_external()?;239240 <PalletNft<T>>::destroy_collection(collection, &cross_sender)241 .map_err(Self::map_unique_err_to_proxy)?;242243 Self::deposit_event(Event::CollectionDestroyed {244 issuer: sender,245 collection_id,246 });247248 Ok(())249 }250251 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]252 #[transactional]253 pub fn change_collection_issuer(254 origin: OriginFor<T>,255 collection_id: RmrkCollectionId,256 new_issuer: <T::Lookup as StaticLookup>::Source,257 ) -> DispatchResult {258 let sender = ensure_signed(origin)?;259260 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;261 collection.check_is_external()?;262263 let new_issuer = T::Lookup::lookup(new_issuer)?;264265 Self::change_collection_owner(266 Self::unique_collection_id(collection_id)?,267 misc::CollectionType::Regular,268 sender.clone(),269 new_issuer.clone(),270 )?;271272 Self::deposit_event(Event::IssuerChanged {273 old_issuer: sender,274 new_issuer,275 collection_id,276 });277278 Ok(())279 }280281 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]282 #[transactional]283 pub fn lock_collection(284 origin: OriginFor<T>,285 collection_id: RmrkCollectionId,286 ) -> DispatchResult {287 let sender = ensure_signed(origin)?;288 let cross_sender = T::CrossAccountId::from_sub(sender.clone());289290 let collection = Self::get_typed_nft_collection(291 Self::unique_collection_id(collection_id)?,292 misc::CollectionType::Regular,293 )?;294 collection.check_is_external()?;295296 Self::check_collection_owner(&collection, &cross_sender)?;297298 let token_count = collection.total_supply();299300 let mut collection = collection.into_inner();301 collection.limits.token_limit = Some(token_count);302 collection.save()?;303304 Self::deposit_event(Event::CollectionLocked {305 issuer: sender,306 collection_id,307 });308309 Ok(())310 }311312 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]313 #[transactional]314 pub fn mint_nft(315 origin: OriginFor<T>,316 owner: T::AccountId,317 collection_id: RmrkCollectionId,318 recipient: Option<T::AccountId>,319 royalty_amount: Option<Permill>,320 metadata: RmrkString,321 transferable: bool,322 ) -> DispatchResult {323 let sender = ensure_signed(origin)?;324 let sender = T::CrossAccountId::from_sub(sender);325 let cross_owner = T::CrossAccountId::from_sub(owner.clone());326327 let collection = Self::get_typed_nft_collection(328 Self::unique_collection_id(collection_id)?,329 misc::CollectionType::Regular,330 )?;331 collection.check_is_external()?;332333 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {334 recipient: recipient.unwrap_or_else(|| owner.clone()),335 amount,336 });337338 let nft_id = Self::create_nft(339 &sender,340 &cross_owner,341 &collection,342 [343 Self::rmrk_property(TokenType, &NftType::Regular)?,344 Self::rmrk_property(Transferable, &transferable)?,345 Self::rmrk_property(PendingNftAccept, &false)?,346 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,347 Self::rmrk_property(Metadata, &metadata)?,348 Self::rmrk_property(Equipped, &false)?,349 Self::rmrk_property(350 ResourceCollection,351 &Self::init_collection(352 sender.clone(),353 CreateCollectionData {354 ..Default::default()355 },356 [Self::rmrk_property(357 CollectionType,358 &misc::CollectionType::Resource,359 )?]360 .into_iter(),361 )?,362 )?, // todo possibly add limits to the collection if rmrk warrants them363 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,364 ]365 .into_iter(),366 )367 .map_err(|err| match err {368 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),369 err => Self::map_unique_err_to_proxy(err),370 })?;371372 Self::deposit_event(Event::NftMinted {373 owner,374 collection_id,375 nft_id: nft_id.0,376 });377378 Ok(())379 }380381 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]382 #[transactional]383 pub fn burn_nft(384 origin: OriginFor<T>,385 collection_id: RmrkCollectionId,386 nft_id: RmrkNftId,387 ) -> DispatchResult {388 let sender = ensure_signed(origin)?;389 let cross_sender = T::CrossAccountId::from_sub(sender.clone());390391 let collection = Self::get_typed_nft_collection(392 Self::unique_collection_id(collection_id)?,393 misc::CollectionType::Regular,394 )?;395 collection.check_is_external()?;396397 Self::destroy_nft(398 cross_sender,399 Self::unique_collection_id(collection_id)?,400 nft_id.into(),401 )402 .map_err(Self::map_unique_err_to_proxy)?;403404 Self::deposit_event(Event::NFTBurned {405 owner: sender,406 nft_id,407 });408409 Ok(())410 }411412 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]413 #[transactional]414 pub fn send(415 origin: OriginFor<T>,416 rmrk_collection_id: RmrkCollectionId,417 rmrk_nft_id: RmrkNftId,418 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,419 ) -> DispatchResult {420 let sender = ensure_signed(origin.clone())?;421 let cross_sender = T::CrossAccountId::from_sub(sender.clone());422423 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;424 let nft_id = rmrk_nft_id.into();425426 let collection =427 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;428 collection.check_is_external()?;429430 let token_data =431 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;432433 let from = token_data.owner;434435 ensure!(436 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,437 <Error<T>>::NonTransferable438 );439440 ensure!(441 !Self::get_nft_property_decoded(442 collection_id,443 nft_id,444 RmrkProperty::PendingNftAccept445 )?,446 <Error<T>>::NoPermission447 );448449 let target_owner;450 let approval_required;451452 match new_owner {453 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {454 target_owner = T::CrossAccountId::from_sub(account_id.clone());455 approval_required = false;456 }457 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(458 target_collection_id,459 target_nft_id,460 ) => {461 let target_collection_id = Self::unique_collection_id(target_collection_id)?;462463 let target_nft_budget = budget::Value::new(NESTING_BUDGET);464465 let target_nft_owner = <PalletStructure<T>>::get_checked_indirect_owner(466 target_collection_id,467 target_nft_id.into(),468 Some((collection_id, nft_id)),469 &target_nft_budget,470 )471 .map_err(Self::map_unique_err_to_proxy)?;472473 approval_required = cross_sender != target_nft_owner;474475 if approval_required {476 target_owner = target_nft_owner;477478 <PalletNft<T>>::set_scoped_token_property(479 collection.id,480 nft_id,481 PropertyScope::Rmrk,482 Self::rmrk_property(PendingNftAccept, &approval_required)?,483 )?;484 } else {485 target_owner = T::CrossTokenAddressMapping::token_to_address(486 target_collection_id,487 target_nft_id.into(),488 );489 }490 }491 }492493 let src_nft_budget = budget::Value::new(NESTING_BUDGET);494495 <PalletNft<T>>::transfer_from(496 &collection,497 &cross_sender,498 &from,499 &target_owner,500 nft_id,501 &src_nft_budget,502 )503 .map_err(Self::map_unique_err_to_proxy)?;504505 Self::deposit_event(Event::NFTSent {506 sender,507 recipient: new_owner,508 collection_id: rmrk_collection_id,509 nft_id: rmrk_nft_id,510 approval_required,511 });512513 Ok(())514 }515516 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]517 #[transactional]518 pub fn accept_nft(519 origin: OriginFor<T>,520 rmrk_collection_id: RmrkCollectionId,521 rmrk_nft_id: RmrkNftId,522 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,523 ) -> DispatchResult {524 let sender = ensure_signed(origin.clone())?;525 let cross_sender = T::CrossAccountId::from_sub(sender.clone());526527 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;528 let nft_id = rmrk_nft_id.into();529530 let collection =531 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;532 collection.check_is_external()?;533534 let new_cross_owner = match new_owner {535 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {536 T::CrossAccountId::from_sub(account_id.clone())537 }538 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(539 target_collection_id,540 target_nft_id,541 ) => {542 let target_collection_id = Self::unique_collection_id(target_collection_id)?;543544 T::CrossTokenAddressMapping::token_to_address(545 target_collection_id,546 TokenId(target_nft_id),547 )548 }549 };550551 let budget = budget::Value::new(NESTING_BUDGET);552553 <PalletNft<T>>::transfer(554 &collection,555 &cross_sender,556 &new_cross_owner,557 nft_id,558 &budget,559 )560 .map_err(|err| {561 if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {562 <Error<T>>::CannotAcceptNonOwnedNft.into()563 } else {564 Self::map_unique_err_to_proxy(err)565 }566 })?;567568 <PalletNft<T>>::set_scoped_token_property(569 collection.id,570 nft_id,571 PropertyScope::Rmrk,572 Self::rmrk_property(PendingNftAccept, &false)?,573 )?;574575 Self::deposit_event(Event::NFTAccepted {576 sender,577 recipient: new_owner,578 collection_id: rmrk_collection_id,579 nft_id: rmrk_nft_id,580 });581582 Ok(())583 }584585 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]586 #[transactional]587 pub fn reject_nft(588 origin: OriginFor<T>,589 rmrk_collection_id: RmrkCollectionId,590 rmrk_nft_id: RmrkNftId,591 ) -> DispatchResult {592 let sender = ensure_signed(origin)?;593 let cross_sender = T::CrossAccountId::from_sub(sender.clone());594595 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;596 let nft_id = rmrk_nft_id.into();597598 let collection =599 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;600 collection.check_is_external()?;601602 Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {603 if err == <CommonError<T>>::NoPermission.into()604 || err == <CommonError<T>>::ApprovedValueTooLow.into()605 {606 <Error<T>>::CannotRejectNonOwnedNft.into()607 } else {608 Self::map_unique_err_to_proxy(err)609 }610 })?;611612 Self::deposit_event(Event::NFTRejected {613 sender,614 collection_id: rmrk_collection_id,615 nft_id: rmrk_nft_id,616 });617618 Ok(())619 }620621 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]622 #[transactional]623 pub fn accept_resource(624 origin: OriginFor<T>,625 rmrk_collection_id: RmrkCollectionId,626 rmrk_nft_id: RmrkNftId,627 rmrk_resource_id: RmrkResourceId,628 ) -> DispatchResult {629 let sender = ensure_signed(origin)?;630 let cross_sender = T::CrossAccountId::from_sub(sender);631632 let collection_id = Self::unique_collection_id(rmrk_collection_id)633 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;634 let collection =635 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;636 collection.check_is_external()?;637638 let nft_id = rmrk_nft_id.into();639 let resource_id = rmrk_resource_id.into();640641 let budget = budget::Value::new(NESTING_BUDGET);642643 let nft_owner =644 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)645 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;646647 let resource_collection_id: CollectionId =648 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)649 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;650651 let is_pending: bool = Self::get_nft_property_decoded(652 resource_collection_id,653 resource_id,654 PendingResourceAccept,655 )656 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;657658 ensure!(is_pending, <Error<T>>::ResourceNotPending);659660 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);661662 <PalletNft<T>>::set_scoped_token_property(663 resource_collection_id,664 rmrk_resource_id.into(),665 PropertyScope::Rmrk,666 Self::rmrk_property(PendingResourceAccept, &false)?,667 )?;668669 Self::deposit_event(Event::<T>::ResourceAccepted {670 nft_id: rmrk_nft_id,671 resource_id: rmrk_resource_id,672 });673674 Ok(())675 }676677 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]678 #[transactional]679 pub fn accept_resource_removal(680 origin: OriginFor<T>,681 rmrk_collection_id: RmrkCollectionId,682 rmrk_nft_id: RmrkNftId,683 rmrk_resource_id: RmrkResourceId,684 ) -> DispatchResult {685 let sender = ensure_signed(origin)?;686 let cross_sender = T::CrossAccountId::from_sub(sender);687688 let collection_id = Self::unique_collection_id(rmrk_collection_id)689 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;690 let collection =691 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;692 collection.check_is_external()?;693694 let nft_id = rmrk_nft_id.into();695 let resource_id = rmrk_resource_id.into();696697 let budget = budget::Value::new(NESTING_BUDGET);698699 let nft_owner =700 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)701 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;702703 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);704705 let resource_collection_id: CollectionId =706 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)707 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;708709 let is_pending: bool = Self::get_nft_property_decoded(710 resource_collection_id,711 resource_id,712 PendingResourceRemoval,713 )714 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;715716 ensure!(is_pending, <Error<T>>::ResourceNotPending);717718 let resource_collection = Self::get_typed_nft_collection(719 resource_collection_id,720 misc::CollectionType::Resource,721 )?;722723 <PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())724 .map_err(Self::map_unique_err_to_proxy)?;725726 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {727 nft_id: rmrk_nft_id,728 resource_id: rmrk_resource_id,729 });730731 Ok(())732 }733734 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]735 #[transactional]736 pub fn set_property(737 origin: OriginFor<T>,738 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,739 maybe_nft_id: Option<RmrkNftId>,740 key: RmrkKeyString,741 value: RmrkValueString,742 ) -> DispatchResult {743 let sender = ensure_signed(origin)?;744 let sender = T::CrossAccountId::from_sub(sender);745746 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;747 let collection =748 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;749 collection.check_is_external()?;750751 let budget = budget::Value::new(NESTING_BUDGET);752753 match maybe_nft_id {754 Some(nft_id) => {755 let token_id: TokenId = nft_id.into();756757 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;758 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;759760 <PalletNft<T>>::set_scoped_token_property(761 collection_id,762 token_id,763 PropertyScope::Rmrk,764 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,765 )?;766 }767 None => {768 let collection = Self::get_typed_nft_collection(769 collection_id,770 misc::CollectionType::Regular,771 )?;772773 Self::check_collection_owner(&collection, &sender)?;774775 <PalletCommon<T>>::set_scoped_collection_property(776 collection_id,777 PropertyScope::Rmrk,778 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,779 )?;780 }781 }782783 Self::deposit_event(Event::PropertySet {784 collection_id: rmrk_collection_id,785 maybe_nft_id,786 key,787 value,788 });789790 Ok(())791 }792793 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]794 #[transactional]795 pub fn set_priority(796 origin: OriginFor<T>,797 rmrk_collection_id: RmrkCollectionId,798 rmrk_nft_id: RmrkNftId,799 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,800 ) -> DispatchResult {801 let sender = ensure_signed(origin)?;802 let sender = T::CrossAccountId::from_sub(sender);803804 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;805 let nft_id = rmrk_nft_id.into();806807 let collection =808 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;809 collection.check_is_external()?;810811 let budget = budget::Value::new(NESTING_BUDGET);812813 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;814 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;815816 <PalletNft<T>>::set_scoped_token_property(817 collection_id,818 nft_id,819 PropertyScope::Rmrk,820 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,821 )?;822823 Self::deposit_event(Event::<T>::PrioritySet {824 collection_id: rmrk_collection_id,825 nft_id: rmrk_nft_id,826 });827828 Ok(())829 }830831 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]832 #[transactional]833 pub fn add_basic_resource(834 origin: OriginFor<T>,835 rmrk_collection_id: RmrkCollectionId,836 nft_id: RmrkNftId,837 resource: RmrkBasicResource,838 ) -> DispatchResult {839 let sender = ensure_signed(origin.clone())?;840841 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;842 let collection =843 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;844 collection.check_is_external()?;845846 let resource_id = Self::resource_add(847 sender,848 collection_id,849 nft_id.into(),850 [851 Self::rmrk_property(TokenType, &NftType::Resource)?,852 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,853 Self::rmrk_property(Src, &resource.src)?,854 Self::rmrk_property(Metadata, &resource.metadata)?,855 Self::rmrk_property(License, &resource.license)?,856 Self::rmrk_property(Thumb, &resource.thumb)?,857 ]858 .into_iter(),859 )?;860861 Self::deposit_event(Event::ResourceAdded {862 nft_id,863 resource_id,864 });865 Ok(())866 }867868 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]869 #[transactional]870 pub fn add_composable_resource(871 origin: OriginFor<T>,872 rmrk_collection_id: RmrkCollectionId,873 nft_id: RmrkNftId,874 _resource_id: RmrkBoundedResource,875 resource: RmrkComposableResource,876 ) -> DispatchResult {877 let sender = ensure_signed(origin.clone())?;878879 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;880 let collection =881 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;882 collection.check_is_external()?;883884 let resource_id = Self::resource_add(885 sender,886 collection_id,887 nft_id.into(),888 [889 Self::rmrk_property(TokenType, &NftType::Resource)?,890 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,891 Self::rmrk_property(Parts, &resource.parts)?,892 Self::rmrk_property(Base, &resource.base)?,893 Self::rmrk_property(Src, &resource.src)?,894 Self::rmrk_property(Metadata, &resource.metadata)?,895 Self::rmrk_property(License, &resource.license)?,896 Self::rmrk_property(Thumb, &resource.thumb)?,897 ]898 .into_iter(),899 )?;900901 Self::deposit_event(Event::ResourceAdded {902 nft_id,903 resource_id,904 });905 Ok(())906 }907908 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]909 #[transactional]910 pub fn add_slot_resource(911 origin: OriginFor<T>,912 rmrk_collection_id: RmrkCollectionId,913 nft_id: RmrkNftId,914 resource: RmrkSlotResource,915 ) -> DispatchResult {916 let sender = ensure_signed(origin.clone())?;917918 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;919 let collection =920 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;921 collection.check_is_external()?;922923 let resource_id = Self::resource_add(924 sender,925 collection_id,926 nft_id.into(),927 [928 Self::rmrk_property(TokenType, &NftType::Resource)?,929 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,930 Self::rmrk_property(Base, &resource.base)?,931 Self::rmrk_property(Src, &resource.src)?,932 Self::rmrk_property(Metadata, &resource.metadata)?,933 Self::rmrk_property(Slot, &resource.slot)?,934 Self::rmrk_property(License, &resource.license)?,935 Self::rmrk_property(Thumb, &resource.thumb)?,936 ]937 .into_iter(),938 )?;939940 Self::deposit_event(Event::ResourceAdded {941 nft_id,942 resource_id,943 });944 Ok(())945 }946947 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]948 #[transactional]949 pub fn remove_resource(950 origin: OriginFor<T>,951 rmrk_collection_id: RmrkCollectionId,952 nft_id: RmrkNftId,953 resource_id: RmrkResourceId,954 ) -> DispatchResult {955 let sender = ensure_signed(origin.clone())?;956957 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;958 let collection =959 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;960 collection.check_is_external()?;961962 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;963964 Self::deposit_event(Event::ResourceRemoval {965 nft_id,966 resource_id,967 });968 Ok(())969 }970 }971}972973impl<T: Config> Pallet<T> {974 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {975 let key = rmrk_key.to_key::<T>()?;976977 let scoped_key = PropertyScope::Rmrk978 .apply(key)979 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;980981 Ok(scoped_key)982 }983984 // todo think about renaming these985 pub fn rmrk_property<E: Encode>(986 rmrk_key: RmrkProperty,987 value: &E,988 ) -> Result<Property, DispatchError> {989 let key = rmrk_key.to_key::<T>()?;990991 let value = value992 .encode()993 .try_into()994 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;995996 let property = Property { key, value };997998 Ok(property)999 }10001001 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1002 vec.decode()1003 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1004 }10051006 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1007 where1008 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1009 {1010 vec.rebind()1011 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1012 }10131014 fn init_collection(1015 sender: T::CrossAccountId,1016 data: CreateCollectionData<T::AccountId>,1017 properties: impl Iterator<Item = Property>,1018 ) -> Result<CollectionId, DispatchError> {1019 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10201021 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1022 return Err(<Error<T>>::NoAvailableCollectionId.into());1023 }10241025 <PalletCommon<T>>::set_scoped_collection_properties(1026 collection_id?,1027 PropertyScope::Rmrk,1028 properties,1029 )?;10301031 collection_id1032 }10331034 pub fn create_nft(1035 sender: &T::CrossAccountId,1036 owner: &T::CrossAccountId,1037 collection: &NonfungibleHandle<T>,1038 properties: impl Iterator<Item = Property>,1039 ) -> Result<TokenId, DispatchError> {1040 let data = CreateNftExData {1041 properties: BoundedVec::default(),1042 owner: owner.clone(),1043 };10441045 let budget = budget::Value::new(NESTING_BUDGET);10461047 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;10481049 let nft_id = <PalletNft<T>>::current_token_id(collection.id);10501051 <PalletNft<T>>::set_scoped_token_properties(1052 collection.id,1053 nft_id,1054 PropertyScope::Rmrk,1055 properties,1056 )?;10571058 Ok(nft_id)1059 }10601061 fn destroy_nft(1062 sender: T::CrossAccountId,1063 collection_id: CollectionId,1064 token_id: TokenId,1065 ) -> DispatchResult {1066 let collection =1067 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;10681069 let token_data =1070 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;10711072 let from = token_data.owner;10731074 let budget = budget::Value::new(NESTING_BUDGET);10751076 <PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1077 }10781079 fn resource_add(1080 sender: T::AccountId,1081 collection_id: CollectionId,1082 token_id: TokenId,1083 resource_properties: impl Iterator<Item = Property>,1084 ) -> Result<RmrkResourceId, DispatchError> {1085 let collection =1086 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1087 ensure!(collection.owner == sender, Error::<T>::NoPermission);10881089 let sender = T::CrossAccountId::from_sub(sender);1090 let budget = budget::Value::new(NESTING_BUDGET);10911092 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1093 .map_err(Self::map_unique_err_to_proxy)?;10941095 let pending = sender != nft_owner;10961097 let resource_collection_id: CollectionId =1098 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1099 let resource_collection =1100 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11011102 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11031104 let resource_id = Self::create_nft(1105 &sender,1106 &nft_owner,1107 &resource_collection,1108 resource_properties.chain(1109 [1110 Self::rmrk_property(PendingResourceAccept, &pending)?,1111 Self::rmrk_property(PendingResourceRemoval, &false)?,1112 ]1113 .into_iter(),1114 ),1115 )1116 .map_err(|err| match err {1117 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1118 err => Self::map_unique_err_to_proxy(err),1119 })?;11201121 Ok(resource_id.0)1122 }11231124 fn resource_remove(1125 sender: T::AccountId,1126 collection_id: CollectionId,1127 nft_id: TokenId,1128 resource_id: TokenId,1129 ) -> DispatchResult {1130 let collection =1131 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1132 ensure!(collection.owner == sender, Error::<T>::NoPermission);11331134 let resource_collection_id: CollectionId =1135 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1136 let resource_collection =1137 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1138 ensure!(1139 <PalletNft<T>>::token_exists(&resource_collection, resource_id),1140 Error::<T>::ResourceDoesntExist1141 );11421143 let budget = up_data_structs::budget::Value::new(10);1144 let topmost_owner =1145 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;11461147 let sender = T::CrossAccountId::from_sub(sender);1148 if topmost_owner == sender {1149 <PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1150 .map_err(Self::map_unique_err_to_proxy)?;1151 } else {1152 <PalletNft<T>>::set_scoped_token_property(1153 resource_collection_id,1154 resource_id,1155 PropertyScope::Rmrk,1156 Self::rmrk_property(PendingResourceRemoval, &true)?,1157 )?;1158 }11591160 Ok(())1161 }11621163 fn change_collection_owner(1164 collection_id: CollectionId,1165 collection_type: misc::CollectionType,1166 sender: T::AccountId,1167 new_owner: T::AccountId,1168 ) -> DispatchResult {1169 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1170 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;11711172 let mut collection = collection.into_inner();11731174 collection.owner = new_owner;1175 collection.save()1176 }11771178 fn check_collection_owner(1179 collection: &NonfungibleHandle<T>,1180 account: &T::CrossAccountId,1181 ) -> DispatchResult {1182 collection1183 .check_is_owner(account)1184 .map_err(Self::map_unique_err_to_proxy)1185 }11861187 pub fn last_collection_idx() -> RmrkCollectionId {1188 <CollectionIndex<T>>::get()1189 }11901191 pub fn unique_collection_id(1192 rmrk_collection_id: RmrkCollectionId,1193 ) -> Result<CollectionId, DispatchError> {1194 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1195 .map_err(|_| <Error<T>>::CollectionUnknown.into())1196 }11971198 pub fn rmrk_collection_id(1199 unique_collection_id: CollectionId,1200 ) -> Result<RmrkCollectionId, DispatchError> {1201 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1202 .map_err(|_| <Error<T>>::CollectionUnknown.into())1203 }12041205 pub fn get_nft_collection(1206 collection_id: CollectionId,1207 ) -> Result<NonfungibleHandle<T>, DispatchError> {1208 let collection = <CollectionHandle<T>>::try_get(collection_id)1209 .map_err(|_| <Error<T>>::CollectionUnknown)?;12101211 match collection.mode {1212 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1213 _ => Err(<Error<T>>::CollectionUnknown.into()),1214 }1215 }12161217 pub fn collection_exists(collection_id: CollectionId) -> bool {1218 <CollectionHandle<T>>::try_get(collection_id).is_ok()1219 }12201221 pub fn get_collection_property(1222 collection_id: CollectionId,1223 key: RmrkProperty,1224 ) -> Result<PropertyValue, DispatchError> {1225 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1226 .get(&Self::rmrk_property_key(key)?)1227 .ok_or(<Error<T>>::CollectionUnknown)?1228 .clone();12291230 Ok(collection_property)1231 }12321233 pub fn get_collection_property_decoded<V: Decode>(1234 collection_id: CollectionId,1235 key: RmrkProperty,1236 ) -> Result<V, DispatchError> {1237 Self::decode_property(Self::get_collection_property(collection_id, key)?)1238 }12391240 pub fn get_collection_type(1241 collection_id: CollectionId,1242 ) -> Result<misc::CollectionType, DispatchError> {1243 Self::get_collection_property_decoded(collection_id, CollectionType)1244 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1245 }12461247 pub fn ensure_collection_type(1248 collection_id: CollectionId,1249 collection_type: misc::CollectionType,1250 ) -> DispatchResult {1251 let actual_type = Self::get_collection_type(collection_id)?;1252 ensure!(1253 actual_type == collection_type,1254 <CommonError<T>>::NoPermission1255 );12561257 Ok(())1258 }12591260 pub fn get_typed_nft_collection(1261 collection_id: CollectionId,1262 collection_type: misc::CollectionType,1263 ) -> Result<NonfungibleHandle<T>, DispatchError> {1264 Self::ensure_collection_type(collection_id, collection_type)?;12651266 Self::get_nft_collection(collection_id)1267 }12681269 pub fn get_typed_nft_collection_mapped(1270 rmrk_collection_id: RmrkCollectionId,1271 collection_type: misc::CollectionType,1272 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1273 let unique_collection_id = match collection_type {1274 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1275 _ => rmrk_collection_id.into(),1276 };12771278 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;12791280 Ok((collection, unique_collection_id))1281 }12821283 pub fn get_nft_property(1284 collection_id: CollectionId,1285 nft_id: TokenId,1286 key: RmrkProperty,1287 ) -> Result<PropertyValue, DispatchError> {1288 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1289 .get(&Self::rmrk_property_key(key)?)1290 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1291 .clone();12921293 Ok(nft_property)1294 }12951296 pub fn get_nft_property_decoded<V: Decode>(1297 collection_id: CollectionId,1298 nft_id: TokenId,1299 key: RmrkProperty,1300 ) -> Result<V, DispatchError> {1301 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1302 }13031304 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1305 <TokenData<T>>::contains_key((collection_id, nft_id))1306 }13071308 pub fn get_nft_type(1309 collection_id: CollectionId,1310 token_id: TokenId,1311 ) -> Result<NftType, DispatchError> {1312 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1313 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1314 }13151316 pub fn ensure_nft_type(1317 collection_id: CollectionId,1318 token_id: TokenId,1319 nft_type: NftType,1320 ) -> DispatchResult {1321 let actual_type = Self::get_nft_type(collection_id, token_id)?;1322 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13231324 Ok(())1325 }13261327 pub fn ensure_nft_owner(1328 collection_id: CollectionId,1329 token_id: TokenId,1330 possible_owner: &T::CrossAccountId,1331 nesting_budget: &dyn budget::Budget,1332 ) -> DispatchResult {1333 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1334 possible_owner.clone(),1335 collection_id,1336 token_id,1337 None,1338 nesting_budget,1339 )1340 .map_err(Self::map_unique_err_to_proxy)?;13411342 ensure!(is_owned, <Error<T>>::NoPermission);13431344 Ok(())1345 }13461347 pub fn filter_user_properties<Key, Value, R, Mapper>(1348 collection_id: CollectionId,1349 token_id: Option<TokenId>,1350 filter_keys: Option<Vec<RmrkPropertyKey>>,1351 mapper: Mapper,1352 ) -> Result<Vec<R>, DispatchError>1353 where1354 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1355 Value: Decode + Default,1356 Mapper: Fn(Key, Value) -> R,1357 {1358 filter_keys1359 .map(|keys| {1360 let properties = keys1361 .into_iter()1362 .filter_map(|key| {1363 let key: Key = key.try_into().ok()?;13641365 let value = match token_id {1366 Some(token_id) => Self::get_nft_property_decoded(1367 collection_id,1368 token_id,1369 UserProperty(key.as_ref()),1370 ),1371 None => Self::get_collection_property_decoded(1372 collection_id,1373 UserProperty(key.as_ref()),1374 ),1375 }1376 .ok()?;13771378 Some(mapper(key, value))1379 })1380 .collect();13811382 Ok(properties)1383 })1384 .unwrap_or_else(|| {1385 let properties =1386 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();13871388 Ok(properties)1389 })1390 }13911392 pub fn iterate_user_properties<Key, Value, R, Mapper>(1393 collection_id: CollectionId,1394 token_id: Option<TokenId>,1395 mapper: Mapper,1396 ) -> Result<impl Iterator<Item = R>, DispatchError>1397 where1398 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1399 Value: Decode + Default,1400 Mapper: Fn(Key, Value) -> R,1401 {1402 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14031404 let properties = match token_id {1405 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1406 None => <PalletCommon<T>>::collection_properties(collection_id),1407 };14081409 let properties = properties.into_iter().filter_map(move |(key, value)| {1410 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14111412 let key: Key = key.to_vec().try_into().ok()?;1413 let value: Value = value.decode().ok()?;14141415 Some(mapper(key, value))1416 });14171418 Ok(properties)1419 }14201421 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1422 map_unique_err_to_proxy! {1423 match err {1424 CommonError::NoPermission => NoPermission,1425 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1426 CommonError::PublicMintingNotAllowed => NoPermission,1427 CommonError::TokenNotFound => NoAvailableNftId,1428 CommonError::ApprovedValueTooLow => NoPermission,1429 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1430 StructureError::TokenNotFound => NoAvailableNftId,1431 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1432 }1433 }1434 }1435}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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult};20use frame_system::{pallet_prelude::*, ensure_signed};21use sp_runtime::{DispatchError, Permill, traits::StaticLookup};22use sp_std::vec::Vec;23use up_data_structs::{*, mapping::TokenAddressMapping};24use pallet_common::{25 Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,26};27use pallet_nonfungible::{Pallet as PalletNft, NonfungibleHandle, TokenData};28use pallet_structure::{Pallet as PalletStructure, Error as StructureError};29use pallet_evm::account::CrossAccountId;30use core::convert::AsRef;3132pub use pallet::*;3334#[cfg(feature = "runtime-benchmarks")]35pub mod benchmarking;36pub mod misc;37pub mod property;38pub mod weights;3940pub type SelfWeightOf<T> = <T as Config>::WeightInfo;4142use weights::WeightInfo;43use misc::*;44pub use property::*;4546use RmrkProperty::*;4748const NESTING_BUDGET: u32 = 5;4950#[frame_support::pallet]51pub mod pallet {52 use super::*;53 use pallet_evm::account;5455 #[pallet::config]56 pub trait Config:57 frame_system::Config + pallet_common::Config + pallet_nonfungible::Config + account::Config58 {59 type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;60 type WeightInfo: WeightInfo;61 }6263 #[pallet::storage]64 #[pallet::getter(fn collection_index)]65 pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;6667 #[pallet::storage]68 pub type UniqueCollectionId<T: Config> =69 StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;7071 #[pallet::storage]72 pub type RmrkInernalCollectionId<T: Config> =73 StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;7475 #[pallet::pallet]76 #[pallet::generate_store(pub(super) trait Store)]77 pub struct Pallet<T>(_);7879 #[pallet::event]80 #[pallet::generate_deposit(pub(super) fn deposit_event)]81 pub enum Event<T: Config> {82 CollectionCreated {83 issuer: T::AccountId,84 collection_id: RmrkCollectionId,85 },86 CollectionDestroyed {87 issuer: T::AccountId,88 collection_id: RmrkCollectionId,89 },90 IssuerChanged {91 old_issuer: T::AccountId,92 new_issuer: T::AccountId,93 collection_id: RmrkCollectionId,94 },95 CollectionLocked {96 issuer: T::AccountId,97 collection_id: RmrkCollectionId,98 },99 NftMinted {100 owner: T::AccountId,101 collection_id: RmrkCollectionId,102 nft_id: RmrkNftId,103 },104 NFTBurned {105 owner: T::AccountId,106 nft_id: RmrkNftId,107 },108 NFTSent {109 sender: T::AccountId,110 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,111 collection_id: RmrkCollectionId,112 nft_id: RmrkNftId,113 approval_required: bool,114 },115 NFTAccepted {116 sender: T::AccountId,117 recipient: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,118 collection_id: RmrkCollectionId,119 nft_id: RmrkNftId,120 },121 NFTRejected {122 sender: T::AccountId,123 collection_id: RmrkCollectionId,124 nft_id: RmrkNftId,125 },126 PropertySet {127 collection_id: RmrkCollectionId,128 maybe_nft_id: Option<RmrkNftId>,129 key: RmrkKeyString,130 value: RmrkValueString,131 },132 ResourceAdded {133 nft_id: RmrkNftId,134 resource_id: RmrkResourceId,135 },136 ResourceRemoval {137 nft_id: RmrkNftId,138 resource_id: RmrkResourceId,139 },140 ResourceAccepted {141 nft_id: RmrkNftId,142 resource_id: RmrkResourceId,143 },144 ResourceRemovalAccepted {145 nft_id: RmrkNftId,146 resource_id: RmrkResourceId,147 },148 PrioritySet {149 collection_id: RmrkCollectionId,150 nft_id: RmrkNftId,151 },152 }153154 #[pallet::error]155 pub enum Error<T> {156 /* Unique-specific events */157 CorruptedCollectionType,158 NftTypeEncodeError,159 RmrkPropertyKeyIsTooLong,160 RmrkPropertyValueIsTooLong,161162 /* RMRK compatible events */163 CollectionNotEmpty,164 NoAvailableCollectionId,165 NoAvailableNftId,166 CollectionUnknown,167 NoPermission,168 NonTransferable,169 CollectionFullOrLocked,170 ResourceDoesntExist,171 CannotSendToDescendentOrSelf,172 CannotAcceptNonOwnedNft,173 CannotRejectNonOwnedNft,174 ResourceNotPending,175 }176177 #[pallet::call]178 impl<T: Config> Pallet<T> {179 /// Create a collection180 #[transactional]181 #[pallet::weight(<SelfWeightOf<T>>::create_collection())]182 pub fn create_collection(183 origin: OriginFor<T>,184 metadata: RmrkString,185 max: Option<u32>,186 symbol: RmrkCollectionSymbol,187 ) -> DispatchResult {188 let sender = ensure_signed(origin)?;189190 let limits = CollectionLimits {191 owner_can_transfer: Some(false),192 token_limit: max,193 ..Default::default()194 };195196 let data = CreateCollectionData {197 limits: Some(limits),198 token_prefix: symbol199 .into_inner()200 .try_into()201 .map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,202 permissions: Some(CollectionPermissions {203 nesting: Some(NestingRule::Owner),204 ..Default::default()205 }),206 ..Default::default()207 };208209 let unique_collection_id = Self::init_collection(210 T::CrossAccountId::from_sub(sender.clone()),211 data,212 [213 Self::rmrk_property(Metadata, &metadata)?,214 Self::rmrk_property(CollectionType, &misc::CollectionType::Regular)?,215 ]216 .into_iter(),217 )?;218 let rmrk_collection_id = <CollectionIndex<T>>::get();219220 <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);221 <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);222223 <CollectionIndex<T>>::mutate(|n| *n += 1);224225 Self::deposit_event(Event::CollectionCreated {226 issuer: sender,227 collection_id: rmrk_collection_id,228 });229230 Ok(())231 }232233 /// destroy collection234 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]235 #[transactional]236 pub fn destroy_collection(237 origin: OriginFor<T>,238 collection_id: RmrkCollectionId,239 ) -> DispatchResult {240 let sender = ensure_signed(origin)?;241 let cross_sender = T::CrossAccountId::from_sub(sender.clone());242243 let collection = Self::get_typed_nft_collection(244 Self::unique_collection_id(collection_id)?,245 misc::CollectionType::Regular,246 )?;247 collection.check_is_external()?;248249 <PalletNft<T>>::destroy_collection(collection, &cross_sender)250 .map_err(Self::map_unique_err_to_proxy)?;251252 Self::deposit_event(Event::CollectionDestroyed {253 issuer: sender,254 collection_id,255 });256257 Ok(())258 }259260 /// Change the issuer of a collection261 ///262 /// Parameters:263 /// - `origin`: sender of the transaction264 /// - `collection_id`: collection id of the nft to change issuer of265 /// - `new_issuer`: Collection's new issuer266 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]267 #[transactional]268 pub fn change_collection_issuer(269 origin: OriginFor<T>,270 collection_id: RmrkCollectionId,271 new_issuer: <T::Lookup as StaticLookup>::Source,272 ) -> DispatchResult {273 let sender = ensure_signed(origin)?;274275 let collection = Self::get_nft_collection(Self::unique_collection_id(collection_id)?)?;276 collection.check_is_external()?;277278 let new_issuer = T::Lookup::lookup(new_issuer)?;279280 Self::change_collection_owner(281 Self::unique_collection_id(collection_id)?,282 misc::CollectionType::Regular,283 sender.clone(),284 new_issuer.clone(),285 )?;286287 Self::deposit_event(Event::IssuerChanged {288 old_issuer: sender,289 new_issuer,290 collection_id,291 });292293 Ok(())294 }295296 /// lock collection297 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]298 #[transactional]299 pub fn lock_collection(300 origin: OriginFor<T>,301 collection_id: RmrkCollectionId,302 ) -> DispatchResult {303 let sender = ensure_signed(origin)?;304 let cross_sender = T::CrossAccountId::from_sub(sender.clone());305306 let collection = Self::get_typed_nft_collection(307 Self::unique_collection_id(collection_id)?,308 misc::CollectionType::Regular,309 )?;310 collection.check_is_external()?;311312 Self::check_collection_owner(&collection, &cross_sender)?;313314 let token_count = collection.total_supply();315316 let mut collection = collection.into_inner();317 collection.limits.token_limit = Some(token_count);318 collection.save()?;319320 Self::deposit_event(Event::CollectionLocked {321 issuer: sender,322 collection_id,323 });324325 Ok(())326 }327328 /// Mints an NFT in the specified collection329 /// Sets metadata and the royalty attribute330 ///331 /// Parameters:332 /// - `collection_id`: The class of the asset to be minted.333 /// - `nft_id`: The nft value of the asset to be minted.334 /// - `recipient`: Receiver of the royalty335 /// - `royalty`: Permillage reward from each trade for the Recipient336 /// - `metadata`: Arbitrary data about an nft, e.g. IPFS hash337 /// - `transferable`: Ability to transfer this NFT338 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]339 #[transactional]340 pub fn mint_nft(341 origin: OriginFor<T>,342 owner: T::AccountId,343 collection_id: RmrkCollectionId,344 recipient: Option<T::AccountId>,345 royalty_amount: Option<Permill>,346 metadata: RmrkString,347 transferable: bool,348 ) -> DispatchResult {349 let sender = ensure_signed(origin)?;350 let sender = T::CrossAccountId::from_sub(sender);351 let cross_owner = T::CrossAccountId::from_sub(owner.clone());352353 let collection = Self::get_typed_nft_collection(354 Self::unique_collection_id(collection_id)?,355 misc::CollectionType::Regular,356 )?;357 collection.check_is_external()?;358359 let royalty_info = royalty_amount.map(|amount| rmrk_traits::RoyaltyInfo {360 recipient: recipient.unwrap_or_else(|| owner.clone()),361 amount,362 });363364 let nft_id = Self::create_nft(365 &sender,366 &cross_owner,367 &collection,368 [369 Self::rmrk_property(TokenType, &NftType::Regular)?,370 Self::rmrk_property(Transferable, &transferable)?,371 Self::rmrk_property(PendingNftAccept, &false)?,372 Self::rmrk_property(RoyaltyInfo, &royalty_info)?,373 Self::rmrk_property(Metadata, &metadata)?,374 Self::rmrk_property(Equipped, &false)?,375 Self::rmrk_property(376 ResourceCollection,377 &Self::init_collection(378 sender.clone(),379 CreateCollectionData {380 ..Default::default()381 },382 [Self::rmrk_property(383 CollectionType,384 &misc::CollectionType::Resource,385 )?]386 .into_iter(),387 )?,388 )?, // todo possibly add limits to the collection if rmrk warrants them389 Self::rmrk_property(ResourcePriorities, &<Vec<u8>>::new())?,390 ]391 .into_iter(),392 )393 .map_err(|err| match err {394 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),395 err => Self::map_unique_err_to_proxy(err),396 })?;397398 Self::deposit_event(Event::NftMinted {399 owner,400 collection_id,401 nft_id: nft_id.0,402 });403404 Ok(())405 }406407 /// burn nft408 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]409 #[transactional]410 pub fn burn_nft(411 origin: OriginFor<T>,412 collection_id: RmrkCollectionId,413 nft_id: RmrkNftId,414 ) -> DispatchResult {415 let sender = ensure_signed(origin)?;416 let cross_sender = T::CrossAccountId::from_sub(sender.clone());417418 let collection = Self::get_typed_nft_collection(419 Self::unique_collection_id(collection_id)?,420 misc::CollectionType::Regular,421 )?;422 collection.check_is_external()?;423424 Self::destroy_nft(425 cross_sender,426 Self::unique_collection_id(collection_id)?,427 nft_id.into(),428 )429 .map_err(Self::map_unique_err_to_proxy)?;430431 Self::deposit_event(Event::NFTBurned {432 owner: sender,433 nft_id,434 });435436 Ok(())437 }438439 /// Transfers a NFT from an Account or NFT A to another Account or NFT B440 ///441 /// Parameters:442 /// - `origin`: sender of the transaction443 /// - `rmrk_collection_id`: collection id of the nft to be transferred444 /// - `rmrk_nft_id`: nft id of the nft to be transferred445 /// - `new_owner`: new owner of the nft which can be either an account or a NFT446 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]447 #[transactional]448 pub fn send(449 origin: OriginFor<T>,450 rmrk_collection_id: RmrkCollectionId,451 rmrk_nft_id: RmrkNftId,452 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,453 ) -> DispatchResult {454 let sender = ensure_signed(origin.clone())?;455 let cross_sender = T::CrossAccountId::from_sub(sender.clone());456457 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;458 let nft_id = rmrk_nft_id.into();459460 let collection =461 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;462 collection.check_is_external()?;463464 let token_data =465 <TokenData<T>>::get((collection_id, nft_id)).ok_or(<Error<T>>::NoAvailableNftId)?;466467 let from = token_data.owner;468469 ensure!(470 Self::get_nft_property_decoded(collection_id, nft_id, RmrkProperty::Transferable)?,471 <Error<T>>::NonTransferable472 );473474 ensure!(475 !Self::get_nft_property_decoded(476 collection_id,477 nft_id,478 RmrkProperty::PendingNftAccept479 )?,480 <Error<T>>::NoPermission481 );482483 let target_owner;484 let approval_required;485486 match new_owner {487 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {488 target_owner = T::CrossAccountId::from_sub(account_id.clone());489 approval_required = false;490 }491 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(492 target_collection_id,493 target_nft_id,494 ) => {495 let target_collection_id = Self::unique_collection_id(target_collection_id)?;496497 let target_nft_budget = budget::Value::new(NESTING_BUDGET);498499 let target_nft_owner = <PalletStructure<T>>::get_checked_topmost_owner(500 target_collection_id,501 target_nft_id.into(),502 Some((collection_id, nft_id)),503 &target_nft_budget,504 )505 .map_err(Self::map_unique_err_to_proxy)?;506507 approval_required = cross_sender != target_nft_owner;508509 if approval_required {510 target_owner = target_nft_owner;511512 <PalletNft<T>>::set_scoped_token_property(513 collection.id,514 nft_id,515 PropertyScope::Rmrk,516 Self::rmrk_property(PendingNftAccept, &approval_required)?,517 )?;518 } else {519 target_owner = T::CrossTokenAddressMapping::token_to_address(520 target_collection_id,521 target_nft_id.into(),522 );523 }524 }525 }526527 let src_nft_budget = budget::Value::new(NESTING_BUDGET);528529 <PalletNft<T>>::transfer_from(530 &collection,531 &cross_sender,532 &from,533 &target_owner,534 nft_id,535 &src_nft_budget,536 )537 .map_err(Self::map_unique_err_to_proxy)?;538539 Self::deposit_event(Event::NFTSent {540 sender,541 recipient: new_owner,542 collection_id: rmrk_collection_id,543 nft_id: rmrk_nft_id,544 approval_required,545 });546547 Ok(())548 }549550 /// Accepts an NFT sent from another account to self or owned NFT551 ///552 /// Parameters:553 /// - `origin`: sender of the transaction554 /// - `rmrk_collection_id`: collection id of the nft to be accepted555 /// - `rmrk_nft_id`: nft id of the nft to be accepted556 /// - `new_owner`: either origin's account ID or origin-owned NFT, whichever the NFT was557 /// sent to558 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]559 #[transactional]560 pub fn accept_nft(561 origin: OriginFor<T>,562 rmrk_collection_id: RmrkCollectionId,563 rmrk_nft_id: RmrkNftId,564 new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,565 ) -> DispatchResult {566 let sender = ensure_signed(origin.clone())?;567 let cross_sender = T::CrossAccountId::from_sub(sender.clone());568569 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;570 let nft_id = rmrk_nft_id.into();571572 let collection =573 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;574 collection.check_is_external()?;575576 let new_cross_owner = match new_owner {577 RmrkAccountIdOrCollectionNftTuple::AccountId(ref account_id) => {578 T::CrossAccountId::from_sub(account_id.clone())579 }580 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(581 target_collection_id,582 target_nft_id,583 ) => {584 let target_collection_id = Self::unique_collection_id(target_collection_id)?;585586 T::CrossTokenAddressMapping::token_to_address(587 target_collection_id,588 TokenId(target_nft_id),589 )590 }591 };592593 let budget = budget::Value::new(NESTING_BUDGET);594595 <PalletNft<T>>::transfer(596 &collection,597 &cross_sender,598 &new_cross_owner,599 nft_id,600 &budget,601 )602 .map_err(|err| {603 if err == <CommonError<T>>::OnlyOwnerAllowedToNest.into() {604 <Error<T>>::CannotAcceptNonOwnedNft.into()605 } else {606 Self::map_unique_err_to_proxy(err)607 }608 })?;609610 <PalletNft<T>>::set_scoped_token_property(611 collection.id,612 nft_id,613 PropertyScope::Rmrk,614 Self::rmrk_property(PendingNftAccept, &false)?,615 )?;616617 Self::deposit_event(Event::NFTAccepted {618 sender,619 recipient: new_owner,620 collection_id: rmrk_collection_id,621 nft_id: rmrk_nft_id,622 });623624 Ok(())625 }626627 /// Rejects an NFT sent from another account to self or owned NFT628 ///629 /// Parameters:630 /// - `origin`: sender of the transaction631 /// - `rmrk_collection_id`: collection id of the nft to be accepted632 /// - `rmrk_nft_id`: nft id of the nft to be accepted633 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]634 #[transactional]635 pub fn reject_nft(636 origin: OriginFor<T>,637 rmrk_collection_id: RmrkCollectionId,638 rmrk_nft_id: RmrkNftId,639 ) -> DispatchResult {640 let sender = ensure_signed(origin)?;641 let cross_sender = T::CrossAccountId::from_sub(sender.clone());642643 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;644 let nft_id = rmrk_nft_id.into();645646 let collection =647 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;648 collection.check_is_external()?;649650 Self::destroy_nft(cross_sender, collection_id, nft_id).map_err(|err| {651 if err == <CommonError<T>>::NoPermission.into()652 || err == <CommonError<T>>::ApprovedValueTooLow.into()653 {654 <Error<T>>::CannotRejectNonOwnedNft.into()655 } else {656 Self::map_unique_err_to_proxy(err)657 }658 })?;659660 Self::deposit_event(Event::NFTRejected {661 sender,662 collection_id: rmrk_collection_id,663 nft_id: rmrk_nft_id,664 });665666 Ok(())667 }668669 /// accept the addition of a new resource to an existing NFT670 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]671 #[transactional]672 pub fn accept_resource(673 origin: OriginFor<T>,674 rmrk_collection_id: RmrkCollectionId,675 rmrk_nft_id: RmrkNftId,676 rmrk_resource_id: RmrkResourceId,677 ) -> DispatchResult {678 let sender = ensure_signed(origin)?;679 let cross_sender = T::CrossAccountId::from_sub(sender);680681 let collection_id = Self::unique_collection_id(rmrk_collection_id)682 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;683 let collection =684 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;685 collection.check_is_external()?;686687 let nft_id = rmrk_nft_id.into();688 let resource_id = rmrk_resource_id.into();689690 let budget = budget::Value::new(NESTING_BUDGET);691692 let nft_owner =693 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)694 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;695696 let resource_collection_id: CollectionId =697 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)698 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;699700 let is_pending: bool = Self::get_nft_property_decoded(701 resource_collection_id,702 resource_id,703 PendingResourceAccept,704 )705 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;706707 ensure!(is_pending, <Error<T>>::ResourceNotPending);708709 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);710711 <PalletNft<T>>::set_scoped_token_property(712 resource_collection_id,713 rmrk_resource_id.into(),714 PropertyScope::Rmrk,715 Self::rmrk_property(PendingResourceAccept, &false)?,716 )?;717718 Self::deposit_event(Event::<T>::ResourceAccepted {719 nft_id: rmrk_nft_id,720 resource_id: rmrk_resource_id,721 });722723 Ok(())724 }725726 /// accept the removal of a resource of an existing NFT727 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]728 #[transactional]729 pub fn accept_resource_removal(730 origin: OriginFor<T>,731 rmrk_collection_id: RmrkCollectionId,732 rmrk_nft_id: RmrkNftId,733 rmrk_resource_id: RmrkResourceId,734 ) -> DispatchResult {735 let sender = ensure_signed(origin)?;736 let cross_sender = T::CrossAccountId::from_sub(sender);737738 let collection_id = Self::unique_collection_id(rmrk_collection_id)739 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;740 let collection =741 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;742 collection.check_is_external()?;743744 let nft_id = rmrk_nft_id.into();745 let resource_id = rmrk_resource_id.into();746747 let budget = budget::Value::new(NESTING_BUDGET);748749 let nft_owner =750 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)751 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;752753 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);754755 let resource_collection_id: CollectionId =756 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)757 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;758759 let is_pending: bool = Self::get_nft_property_decoded(760 resource_collection_id,761 resource_id,762 PendingResourceRemoval,763 )764 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;765766 ensure!(is_pending, <Error<T>>::ResourceNotPending);767768 let resource_collection = Self::get_typed_nft_collection(769 resource_collection_id,770 misc::CollectionType::Resource,771 )?;772773 <PalletNft<T>>::burn(&resource_collection, &cross_sender, rmrk_resource_id.into())774 .map_err(Self::map_unique_err_to_proxy)?;775776 Self::deposit_event(Event::<T>::ResourceRemovalAccepted {777 nft_id: rmrk_nft_id,778 resource_id: rmrk_resource_id,779 });780781 Ok(())782 }783784 /// set a custom value on an NFT785 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]786 #[transactional]787 pub fn set_property(788 origin: OriginFor<T>,789 #[pallet::compact] rmrk_collection_id: RmrkCollectionId,790 maybe_nft_id: Option<RmrkNftId>,791 key: RmrkKeyString,792 value: RmrkValueString,793 ) -> DispatchResult {794 let sender = ensure_signed(origin)?;795 let sender = T::CrossAccountId::from_sub(sender);796797 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;798 let collection =799 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;800 collection.check_is_external()?;801802 let budget = budget::Value::new(NESTING_BUDGET);803804 match maybe_nft_id {805 Some(nft_id) => {806 let token_id: TokenId = nft_id.into();807808 Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;809 Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;810811 <PalletNft<T>>::set_scoped_token_property(812 collection_id,813 token_id,814 PropertyScope::Rmrk,815 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,816 )?;817 }818 None => {819 let collection = Self::get_typed_nft_collection(820 collection_id,821 misc::CollectionType::Regular,822 )?;823824 Self::check_collection_owner(&collection, &sender)?;825826 <PalletCommon<T>>::set_scoped_collection_property(827 collection_id,828 PropertyScope::Rmrk,829 Self::rmrk_property(UserProperty(key.as_slice()), &value)?,830 )?;831 }832 }833834 Self::deposit_event(Event::PropertySet {835 collection_id: rmrk_collection_id,836 maybe_nft_id,837 key,838 value,839 });840841 Ok(())842 }843844 /// set a different order of resource priority845 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]846 #[transactional]847 pub fn set_priority(848 origin: OriginFor<T>,849 rmrk_collection_id: RmrkCollectionId,850 rmrk_nft_id: RmrkNftId,851 priorities: BoundedVec<RmrkResourceId, RmrkMaxPriorities>,852 ) -> DispatchResult {853 let sender = ensure_signed(origin)?;854 let sender = T::CrossAccountId::from_sub(sender);855856 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;857 let nft_id = rmrk_nft_id.into();858859 let collection =860 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;861 collection.check_is_external()?;862863 let budget = budget::Value::new(NESTING_BUDGET);864865 Self::ensure_nft_type(collection_id, nft_id, NftType::Regular)?;866 Self::ensure_nft_owner(collection_id, nft_id, &sender, &budget)?;867868 <PalletNft<T>>::set_scoped_token_property(869 collection_id,870 nft_id,871 PropertyScope::Rmrk,872 Self::rmrk_property(ResourcePriorities, &priorities.into_inner())?,873 )?;874875 Self::deposit_event(Event::<T>::PrioritySet {876 collection_id: rmrk_collection_id,877 nft_id: rmrk_nft_id,878 });879880 Ok(())881 }882883 /// Create basic resource884 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]885 #[transactional]886 pub fn add_basic_resource(887 origin: OriginFor<T>,888 rmrk_collection_id: RmrkCollectionId,889 nft_id: RmrkNftId,890 resource: RmrkBasicResource,891 ) -> DispatchResult {892 let sender = ensure_signed(origin.clone())?;893894 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;895 let collection =896 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;897 collection.check_is_external()?;898899 let resource_id = Self::resource_add(900 sender,901 collection_id,902 nft_id.into(),903 [904 Self::rmrk_property(TokenType, &NftType::Resource)?,905 Self::rmrk_property(ResourceType, &misc::ResourceType::Basic)?,906 Self::rmrk_property(Src, &resource.src)?,907 Self::rmrk_property(Metadata, &resource.metadata)?,908 Self::rmrk_property(License, &resource.license)?,909 Self::rmrk_property(Thumb, &resource.thumb)?,910 ]911 .into_iter(),912 )?;913914 Self::deposit_event(Event::ResourceAdded {915 nft_id,916 resource_id,917 });918 Ok(())919 }920921 /// Create composable resource922 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]923 #[transactional]924 pub fn add_composable_resource(925 origin: OriginFor<T>,926 rmrk_collection_id: RmrkCollectionId,927 nft_id: RmrkNftId,928 _resource_id: RmrkBoundedResource,929 resource: RmrkComposableResource,930 ) -> DispatchResult {931 let sender = ensure_signed(origin.clone())?;932933 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;934 let collection =935 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;936 collection.check_is_external()?;937938 let resource_id = Self::resource_add(939 sender,940 collection_id,941 nft_id.into(),942 [943 Self::rmrk_property(TokenType, &NftType::Resource)?,944 Self::rmrk_property(ResourceType, &misc::ResourceType::Composable)?,945 Self::rmrk_property(Parts, &resource.parts)?,946 Self::rmrk_property(Base, &resource.base)?,947 Self::rmrk_property(Src, &resource.src)?,948 Self::rmrk_property(Metadata, &resource.metadata)?,949 Self::rmrk_property(License, &resource.license)?,950 Self::rmrk_property(Thumb, &resource.thumb)?,951 ]952 .into_iter(),953 )?;954955 Self::deposit_event(Event::ResourceAdded {956 nft_id,957 resource_id,958 });959 Ok(())960 }961962 /// Create slot resource963 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]964 #[transactional]965 pub fn add_slot_resource(966 origin: OriginFor<T>,967 rmrk_collection_id: RmrkCollectionId,968 nft_id: RmrkNftId,969 resource: RmrkSlotResource,970 ) -> DispatchResult {971 let sender = ensure_signed(origin.clone())?;972973 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;974 let collection =975 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;976 collection.check_is_external()?;977978 let resource_id = Self::resource_add(979 sender,980 collection_id,981 nft_id.into(),982 [983 Self::rmrk_property(TokenType, &NftType::Resource)?,984 Self::rmrk_property(ResourceType, &misc::ResourceType::Slot)?,985 Self::rmrk_property(Base, &resource.base)?,986 Self::rmrk_property(Src, &resource.src)?,987 Self::rmrk_property(Metadata, &resource.metadata)?,988 Self::rmrk_property(Slot, &resource.slot)?,989 Self::rmrk_property(License, &resource.license)?,990 Self::rmrk_property(Thumb, &resource.thumb)?,991 ]992 .into_iter(),993 )?;994995 Self::deposit_event(Event::ResourceAdded {996 nft_id,997 resource_id,998 });999 Ok(())1000 }10011002 /// remove resource1003 #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]1004 #[transactional]1005 pub fn remove_resource(1006 origin: OriginFor<T>,1007 rmrk_collection_id: RmrkCollectionId,1008 nft_id: RmrkNftId,1009 resource_id: RmrkResourceId,1010 ) -> DispatchResult {1011 let sender = ensure_signed(origin.clone())?;10121013 let collection_id = Self::unique_collection_id(rmrk_collection_id)?;1014 let collection =1015 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1016 collection.check_is_external()?;10171018 Self::resource_remove(sender, collection_id, nft_id.into(), resource_id.into())?;10191020 Self::deposit_event(Event::ResourceRemoval {1021 nft_id,1022 resource_id,1023 });1024 Ok(())1025 }1026 }1027}10281029impl<T: Config> Pallet<T> {1030 pub fn rmrk_property_key(rmrk_key: RmrkProperty) -> Result<PropertyKey, DispatchError> {1031 let key = rmrk_key.to_key::<T>()?;10321033 let scoped_key = PropertyScope::Rmrk1034 .apply(key)1035 .map_err(|_| <Error<T>>::RmrkPropertyKeyIsTooLong)?;10361037 Ok(scoped_key)1038 }10391040 // todo think about renaming these1041 pub fn rmrk_property<E: Encode>(1042 rmrk_key: RmrkProperty,1043 value: &E,1044 ) -> Result<Property, DispatchError> {1045 let key = rmrk_key.to_key::<T>()?;10461047 let value = value1048 .encode()1049 .try_into()1050 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong)?;10511052 let property = Property { key, value };10531054 Ok(property)1055 }10561057 pub fn decode_property<D: Decode>(vec: PropertyValue) -> Result<D, DispatchError> {1058 vec.decode()1059 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1060 }10611062 pub fn rebind<L, S>(vec: &BoundedVec<u8, L>) -> Result<BoundedVec<u8, S>, DispatchError>1063 where1064 BoundedVec<u8, S>: TryFrom<Vec<u8>>,1065 {1066 vec.rebind()1067 .map_err(|_| <Error<T>>::RmrkPropertyValueIsTooLong.into())1068 }10691070 fn init_collection(1071 sender: T::CrossAccountId,1072 data: CreateCollectionData<T::AccountId>,1073 properties: impl Iterator<Item = Property>,1074 ) -> Result<CollectionId, DispatchError> {1075 let collection_id = <PalletNft<T>>::init_collection(sender, data, true);10761077 if let Err(DispatchError::Arithmetic(_)) = &collection_id {1078 return Err(<Error<T>>::NoAvailableCollectionId.into());1079 }10801081 <PalletCommon<T>>::set_scoped_collection_properties(1082 collection_id?,1083 PropertyScope::Rmrk,1084 properties,1085 )?;10861087 collection_id1088 }10891090 pub fn create_nft(1091 sender: &T::CrossAccountId,1092 owner: &T::CrossAccountId,1093 collection: &NonfungibleHandle<T>,1094 properties: impl Iterator<Item = Property>,1095 ) -> Result<TokenId, DispatchError> {1096 let data = CreateNftExData {1097 properties: BoundedVec::default(),1098 owner: owner.clone(),1099 };11001101 let budget = budget::Value::new(NESTING_BUDGET);11021103 <PalletNft<T>>::create_item(collection, sender, data, &budget)?;11041105 let nft_id = <PalletNft<T>>::current_token_id(collection.id);11061107 <PalletNft<T>>::set_scoped_token_properties(1108 collection.id,1109 nft_id,1110 PropertyScope::Rmrk,1111 properties,1112 )?;11131114 Ok(nft_id)1115 }11161117 fn destroy_nft(1118 sender: T::CrossAccountId,1119 collection_id: CollectionId,1120 token_id: TokenId,1121 ) -> DispatchResult {1122 let collection =1123 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;11241125 let token_data =1126 <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;11271128 let from = token_data.owner;11291130 let budget = budget::Value::new(NESTING_BUDGET);11311132 <PalletNft<T>>::burn_from(&collection, &sender, &from, token_id, &budget)1133 }11341135 fn resource_add(1136 sender: T::AccountId,1137 collection_id: CollectionId,1138 token_id: TokenId,1139 resource_properties: impl Iterator<Item = Property>,1140 ) -> Result<RmrkResourceId, DispatchError> {1141 let collection =1142 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1143 ensure!(collection.owner == sender, Error::<T>::NoPermission);11441145 let sender = T::CrossAccountId::from_sub(sender);1146 let budget = budget::Value::new(NESTING_BUDGET);11471148 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, token_id, &budget)1149 .map_err(Self::map_unique_err_to_proxy)?;11501151 let pending = sender != nft_owner;11521153 let resource_collection_id: CollectionId =1154 Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;1155 let resource_collection =1156 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;11571158 // todo probably add extra connections to bases, slots, etc., when RMRK starts to use them11591160 let resource_id = Self::create_nft(1161 &sender,1162 &nft_owner,1163 &resource_collection,1164 resource_properties.chain(1165 [1166 Self::rmrk_property(PendingResourceAccept, &pending)?,1167 Self::rmrk_property(PendingResourceRemoval, &false)?,1168 ]1169 .into_iter(),1170 ),1171 )1172 .map_err(|err| match err {1173 DispatchError::Arithmetic(_) => <Error<T>>::NoAvailableNftId.into(),1174 err => Self::map_unique_err_to_proxy(err),1175 })?;11761177 Ok(resource_id.0)1178 }11791180 fn resource_remove(1181 sender: T::AccountId,1182 collection_id: CollectionId,1183 nft_id: TokenId,1184 resource_id: TokenId,1185 ) -> DispatchResult {1186 let collection =1187 Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?;1188 ensure!(collection.owner == sender, Error::<T>::NoPermission);11891190 let resource_collection_id: CollectionId =1191 Self::get_nft_property_decoded(collection_id, nft_id, ResourceCollection)?;1192 let resource_collection =1193 Self::get_typed_nft_collection(resource_collection_id, misc::CollectionType::Resource)?;1194 ensure!(1195 <PalletNft<T>>::token_exists(&resource_collection, resource_id),1196 Error::<T>::ResourceDoesntExist1197 );11981199 let budget = up_data_structs::budget::Value::new(10);1200 let topmost_owner =1201 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;12021203 let sender = T::CrossAccountId::from_sub(sender);1204 if topmost_owner == sender {1205 <PalletNft<T>>::burn(&resource_collection, &sender, resource_id)1206 .map_err(Self::map_unique_err_to_proxy)?;1207 } else {1208 <PalletNft<T>>::set_scoped_token_property(1209 resource_collection_id,1210 resource_id,1211 PropertyScope::Rmrk,1212 Self::rmrk_property(PendingResourceRemoval, &true)?,1213 )?;1214 }12151216 Ok(())1217 }12181219 fn change_collection_owner(1220 collection_id: CollectionId,1221 collection_type: misc::CollectionType,1222 sender: T::AccountId,1223 new_owner: T::AccountId,1224 ) -> DispatchResult {1225 let collection = Self::get_typed_nft_collection(collection_id, collection_type)?;1226 Self::check_collection_owner(&collection, &T::CrossAccountId::from_sub(sender))?;12271228 let mut collection = collection.into_inner();12291230 collection.owner = new_owner;1231 collection.save()1232 }12331234 fn check_collection_owner(1235 collection: &NonfungibleHandle<T>,1236 account: &T::CrossAccountId,1237 ) -> DispatchResult {1238 collection1239 .check_is_owner(account)1240 .map_err(Self::map_unique_err_to_proxy)1241 }12421243 pub fn last_collection_idx() -> RmrkCollectionId {1244 <CollectionIndex<T>>::get()1245 }12461247 pub fn unique_collection_id(1248 rmrk_collection_id: RmrkCollectionId,1249 ) -> Result<CollectionId, DispatchError> {1250 <UniqueCollectionId<T>>::try_get(rmrk_collection_id)1251 .map_err(|_| <Error<T>>::CollectionUnknown.into())1252 }12531254 pub fn rmrk_collection_id(1255 unique_collection_id: CollectionId,1256 ) -> Result<RmrkCollectionId, DispatchError> {1257 <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)1258 .map_err(|_| <Error<T>>::CollectionUnknown.into())1259 }12601261 pub fn get_nft_collection(1262 collection_id: CollectionId,1263 ) -> Result<NonfungibleHandle<T>, DispatchError> {1264 let collection = <CollectionHandle<T>>::try_get(collection_id)1265 .map_err(|_| <Error<T>>::CollectionUnknown)?;12661267 match collection.mode {1268 CollectionMode::NFT => Ok(NonfungibleHandle::cast(collection)),1269 _ => Err(<Error<T>>::CollectionUnknown.into()),1270 }1271 }12721273 pub fn collection_exists(collection_id: CollectionId) -> bool {1274 <CollectionHandle<T>>::try_get(collection_id).is_ok()1275 }12761277 pub fn get_collection_property(1278 collection_id: CollectionId,1279 key: RmrkProperty,1280 ) -> Result<PropertyValue, DispatchError> {1281 let collection_property = <PalletCommon<T>>::collection_properties(collection_id)1282 .get(&Self::rmrk_property_key(key)?)1283 .ok_or(<Error<T>>::CollectionUnknown)?1284 .clone();12851286 Ok(collection_property)1287 }12881289 pub fn get_collection_property_decoded<V: Decode>(1290 collection_id: CollectionId,1291 key: RmrkProperty,1292 ) -> Result<V, DispatchError> {1293 Self::decode_property(Self::get_collection_property(collection_id, key)?)1294 }12951296 pub fn get_collection_type(1297 collection_id: CollectionId,1298 ) -> Result<misc::CollectionType, DispatchError> {1299 Self::get_collection_property_decoded(collection_id, CollectionType)1300 .map_err(|_| <Error<T>>::CorruptedCollectionType.into())1301 }13021303 pub fn ensure_collection_type(1304 collection_id: CollectionId,1305 collection_type: misc::CollectionType,1306 ) -> DispatchResult {1307 let actual_type = Self::get_collection_type(collection_id)?;1308 ensure!(1309 actual_type == collection_type,1310 <CommonError<T>>::NoPermission1311 );13121313 Ok(())1314 }13151316 pub fn get_typed_nft_collection(1317 collection_id: CollectionId,1318 collection_type: misc::CollectionType,1319 ) -> Result<NonfungibleHandle<T>, DispatchError> {1320 Self::ensure_collection_type(collection_id, collection_type)?;13211322 Self::get_nft_collection(collection_id)1323 }13241325 pub fn get_typed_nft_collection_mapped(1326 rmrk_collection_id: RmrkCollectionId,1327 collection_type: misc::CollectionType,1328 ) -> Result<(NonfungibleHandle<T>, CollectionId), DispatchError> {1329 let unique_collection_id = match collection_type {1330 misc::CollectionType::Regular => Self::unique_collection_id(rmrk_collection_id)?,1331 _ => rmrk_collection_id.into(),1332 };13331334 let collection = Self::get_typed_nft_collection(unique_collection_id, collection_type)?;13351336 Ok((collection, unique_collection_id))1337 }13381339 pub fn get_nft_property(1340 collection_id: CollectionId,1341 nft_id: TokenId,1342 key: RmrkProperty,1343 ) -> Result<PropertyValue, DispatchError> {1344 let nft_property = <PalletNft<T>>::token_properties((collection_id, nft_id))1345 .get(&Self::rmrk_property_key(key)?)1346 .ok_or(<Error<T>>::NoAvailableNftId)? // todo replace with better error?1347 .clone();13481349 Ok(nft_property)1350 }13511352 pub fn get_nft_property_decoded<V: Decode>(1353 collection_id: CollectionId,1354 nft_id: TokenId,1355 key: RmrkProperty,1356 ) -> Result<V, DispatchError> {1357 Self::decode_property(Self::get_nft_property(collection_id, nft_id, key)?)1358 }13591360 pub fn nft_exists(collection_id: CollectionId, nft_id: TokenId) -> bool {1361 <TokenData<T>>::contains_key((collection_id, nft_id))1362 }13631364 pub fn get_nft_type(1365 collection_id: CollectionId,1366 token_id: TokenId,1367 ) -> Result<NftType, DispatchError> {1368 Self::get_nft_property_decoded(collection_id, token_id, TokenType)1369 .map_err(|_| <Error<T>>::NoAvailableNftId.into())1370 }13711372 pub fn ensure_nft_type(1373 collection_id: CollectionId,1374 token_id: TokenId,1375 nft_type: NftType,1376 ) -> DispatchResult {1377 let actual_type = Self::get_nft_type(collection_id, token_id)?;1378 ensure!(actual_type == nft_type, <Error<T>>::NoPermission);13791380 Ok(())1381 }13821383 pub fn ensure_nft_owner(1384 collection_id: CollectionId,1385 token_id: TokenId,1386 possible_owner: &T::CrossAccountId,1387 nesting_budget: &dyn budget::Budget,1388 ) -> DispatchResult {1389 let is_owned = <PalletStructure<T>>::check_indirectly_owned(1390 possible_owner.clone(),1391 collection_id,1392 token_id,1393 None,1394 nesting_budget,1395 )1396 .map_err(Self::map_unique_err_to_proxy)?;13971398 ensure!(is_owned, <Error<T>>::NoPermission);13991400 Ok(())1401 }14021403 pub fn filter_user_properties<Key, Value, R, Mapper>(1404 collection_id: CollectionId,1405 token_id: Option<TokenId>,1406 filter_keys: Option<Vec<RmrkPropertyKey>>,1407 mapper: Mapper,1408 ) -> Result<Vec<R>, DispatchError>1409 where1410 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1411 Value: Decode + Default,1412 Mapper: Fn(Key, Value) -> R,1413 {1414 filter_keys1415 .map(|keys| {1416 let properties = keys1417 .into_iter()1418 .filter_map(|key| {1419 let key: Key = key.try_into().ok()?;14201421 let value = match token_id {1422 Some(token_id) => Self::get_nft_property_decoded(1423 collection_id,1424 token_id,1425 UserProperty(key.as_ref()),1426 ),1427 None => Self::get_collection_property_decoded(1428 collection_id,1429 UserProperty(key.as_ref()),1430 ),1431 }1432 .ok()?;14331434 Some(mapper(key, value))1435 })1436 .collect();14371438 Ok(properties)1439 })1440 .unwrap_or_else(|| {1441 let properties =1442 Self::iterate_user_properties(collection_id, token_id, mapper)?.collect();14431444 Ok(properties)1445 })1446 }14471448 pub fn iterate_user_properties<Key, Value, R, Mapper>(1449 collection_id: CollectionId,1450 token_id: Option<TokenId>,1451 mapper: Mapper,1452 ) -> Result<impl Iterator<Item = R>, DispatchError>1453 where1454 Key: TryFrom<RmrkPropertyKey> + AsRef<[u8]>,1455 Value: Decode + Default,1456 Mapper: Fn(Key, Value) -> R,1457 {1458 let key_prefix = Self::rmrk_property_key(UserProperty(b""))?;14591460 let properties = match token_id {1461 Some(token_id) => <PalletNft<T>>::token_properties((collection_id, token_id)),1462 None => <PalletCommon<T>>::collection_properties(collection_id),1463 };14641465 let properties = properties.into_iter().filter_map(move |(key, value)| {1466 let key = key.as_slice().strip_prefix(key_prefix.as_slice())?;14671468 let key: Key = key.to_vec().try_into().ok()?;1469 let value: Value = value.decode().ok()?;14701471 Some(mapper(key, value))1472 });14731474 Ok(properties)1475 }14761477 fn map_unique_err_to_proxy(err: DispatchError) -> DispatchError {1478 map_unique_err_to_proxy! {1479 match err {1480 CommonError::NoPermission => NoPermission,1481 CommonError::CollectionTokenLimitExceeded => CollectionFullOrLocked,1482 CommonError::PublicMintingNotAllowed => NoPermission,1483 CommonError::TokenNotFound => NoAvailableNftId,1484 CommonError::ApprovedValueTooLow => NoPermission,1485 CommonError::CantDestroyNotEmptyCollection => CollectionNotEmpty,1486 StructureError::TokenNotFound => NoAvailableNftId,1487 StructureError::OuroborosDetected => CannotSendToDescendentOrSelf,1488 }1489 }1490 }1491}pallets/proxy-rmrk-core/src/misc.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/misc.rs
+++ b/pallets/proxy-rmrk-core/src/misc.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 super::*;
use codec::{Encode, Decode, Error};
pallets/proxy-rmrk-core/src/property.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/property.rs
+++ b/pallets/proxy-rmrk-core/src/property.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 super::*;
use core::convert::AsRef;
pallets/proxy-rmrk-core/src/weights.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/proxy-rmrk-core/src/weights.rs
@@ -0,0 +1,74 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_proxy_rmrk_core
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
+//! DATE: 2022-06-09, STEPS: `50`, REPEAT: 200, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024
+
+// Executed Command:
+// target/release/unique-collator
+// benchmark
+// pallet
+// --pallet
+// pallet-proxy-rmrk-core
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=200
+// --heap-pages=4096
+// --output=./pallets/proxy-rmrk-core/src/weights.rs
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+#![allow(clippy::unnecessary_cast)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_proxy_rmrk_core.
+pub trait WeightInfo {
+ fn create_collection() -> Weight;
+}
+
+/// Weights for pallet_proxy_rmrk_core using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+ // Storage: Common CreatedCollectionCount (r:1 w:1)
+ // Storage: Common DestroyedCollectionCount (r:1 w:0)
+ // Storage: System Account (r:2 w:2)
+ // Storage: RmrkCore CollectionIndex (r:1 w:1)
+ // Storage: Common CollectionPropertyPermissions (r:0 w:1)
+ // Storage: Common CollectionProperties (r:0 w:1)
+ // Storage: Common CollectionById (r:0 w:1)
+ // Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+ // Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+ fn create_collection() -> Weight {
+ (76_647_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(5 as Weight))
+ .saturating_add(T::DbWeight::get().writes(9 as Weight))
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ // Storage: Common CreatedCollectionCount (r:1 w:1)
+ // Storage: Common DestroyedCollectionCount (r:1 w:0)
+ // Storage: System Account (r:2 w:2)
+ // Storage: RmrkCore CollectionIndex (r:1 w:1)
+ // Storage: Common CollectionPropertyPermissions (r:0 w:1)
+ // Storage: Common CollectionProperties (r:0 w:1)
+ // Storage: Common CollectionById (r:0 w:1)
+ // Storage: RmrkCore UniqueCollectionId (r:0 w:1)
+ // Storage: RmrkCore RmrkInernalCollectionId (r:0 w:1)
+ fn create_collection() -> Weight {
+ (76_647_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(5 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(9 as Weight))
+ }
+}
pallets/proxy-rmrk-equip/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-equip/src/lib.rs
+++ b/pallets/proxy-rmrk-equip/src/lib.rs
@@ -74,6 +74,15 @@
#[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)
+ ///
+ /// 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
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
#[transactional]
pub fn create_base(
@@ -137,6 +146,19 @@
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
+ /// 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
+ /// array of [key, value, inherit].
+ /// - key: arbitrary BoundedString, defined by client
+ /// - value: arbitrary BoundedString, defined by client
+ /// - inherit: optional bool
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
#[transactional]
pub fn theme_add(
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -149,7 +149,7 @@
})
}
- pub fn get_checked_indirect_owner(
+ pub fn get_checked_topmost_owner(
collection: CollectionId,
token: TokenId,
for_nest: Option<(CollectionId, TokenId)>,
@@ -203,7 +203,7 @@
None => user,
};
- Self::get_checked_indirect_owner(collection, token, for_nest, budget)
+ Self::get_checked_topmost_owner(collection, token, for_nest, budget)
.map(|indirect_owner| indirect_owner == target_parent)
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -44,7 +44,7 @@
};
use pallet_evm::account::CrossAccountId;
use pallet_common::{
- CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_call,
+ CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx,
dispatch::CollectionDispatch,
};
pub mod eth;
@@ -581,7 +581,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.create_item(sender, owner, data, &budget))
}
/// This method creates multiple items in a collection created with CreateCollection method.
@@ -609,7 +609,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget))
}
#[weight = T::CommonWeightInfo::set_collection_properties(properties.len() as u32)]
@@ -623,7 +623,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))
+ dispatch_tx::<T, _>(collection_id, |d| d.set_collection_properties(sender, properties))
}
#[weight = T::CommonWeightInfo::delete_collection_properties(property_keys.len() as u32)]
@@ -637,7 +637,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))
+ dispatch_tx::<T, _>(collection_id, |d| d.delete_collection_properties(&sender, property_keys))
}
#[weight = T::CommonWeightInfo::set_token_properties(properties.len() as u32)]
@@ -652,7 +652,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))
+ dispatch_tx::<T, _>(collection_id, |d| d.set_token_properties(sender, token_id, properties))
}
#[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)]
@@ -667,7 +667,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))
+ dispatch_tx::<T, _>(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys))
}
#[weight = T::CommonWeightInfo::set_property_permissions(property_permissions.len() as u32)]
@@ -681,7 +681,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))
+ dispatch_tx::<T, _>(collection_id, |d| d.set_property_permissions(&sender, property_permissions))
}
#[weight = T::CommonWeightInfo::create_multiple_items_ex(&data)]
@@ -690,7 +690,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget))
}
// TODO! transaction weight
@@ -738,7 +738,7 @@
pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;
+ let post_info = dispatch_tx::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;
if value == 1 {
<NftTransferBasket<T>>::remove(collection_id, item_id);
<NftApproveBasket<T>>::remove(collection_id, item_id);
@@ -771,7 +771,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value, &budget))
}
/// Change ownership of the token.
@@ -803,7 +803,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget))
}
/// Set, change, or remove approved address to transfer the ownership of the NFT.
@@ -826,7 +826,7 @@
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
- dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
+ dispatch_tx::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
}
/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
@@ -854,7 +854,7 @@
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))
+ dispatch_tx::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value, &budget))
}
#[weight = <SelfWeightOf<T>>::set_collection_limits()]
primitives/rmrk-rpc/src/lib.rsdiffbeforeafterboth--- a/primitives/rmrk-rpc/src/lib.rs
+++ b/primitives/rmrk-rpc/src/lib.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/>.
+
#![cfg_attr(not(feature = "std"), no_std)]
use sp_api::{Encode, Decode};
primitives/rmrk-traits/src/base.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/base.rs
+++ b/primitives/rmrk-traits/src/base.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/collection.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/collection.rs
+++ b/primitives/rmrk-traits/src/collection.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/lib.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/lib.rs
+++ b/primitives/rmrk-traits/src/lib.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
#![cfg_attr(not(feature = "std"), no_std)]
pub mod base;
primitives/rmrk-traits/src/nft.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/nft.rs
+++ b/primitives/rmrk-traits/src/nft.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/part.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/part.rs
+++ b/primitives/rmrk-traits/src/part.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/property.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/property.rs
+++ b/primitives/rmrk-traits/src/property.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/resource.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/resource.rs
+++ b/primitives/rmrk-traits/src/resource.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode, MaxEncodedLen};
use scale_info::TypeInfo;
primitives/rmrk-traits/src/serialize.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/serialize.rs
+++ b/primitives/rmrk-traits/src/serialize.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use core::convert::AsRef;
use serde::ser::{self, Serialize};
primitives/rmrk-traits/src/theme.rsdiffbeforeafterboth--- a/primitives/rmrk-traits/src/theme.rs
+++ b/primitives/rmrk-traits/src/theme.rs
@@ -1,3 +1,7 @@
+// Copyright (C) 2021-2022 RMRK
+// This file is part of rmrk-substrate.
+// License: Apache 2.0 modified by RMRK, see https://github.com/rmrk-team/rmrk-substrate/blob/main/LICENSE
+
use codec::{Decode, Encode};
use scale_info::TypeInfo;
runtime/common/src/constants.rsdiffbeforeafterboth--- a/runtime/common/src/constants.rs
+++ b/runtime/common/src/constants.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_runtime::Perbill;
use frame_support::{
parameter_types,
runtime/common/src/dispatch.rsdiffbeforeafterboth--- a/runtime/common/src/dispatch.rs
+++ b/runtime/common/src/dispatch.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 frame_support::{dispatch::DispatchResult, ensure};
use pallet_evm::{PrecompileHandle, PrecompileResult};
use sp_core::H160;
runtime/common/src/lib.rsdiffbeforeafterboth--- a/runtime/common/src/lib.rs
+++ b/runtime/common/src/lib.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/>.
+
#![cfg_attr(not(feature = "std"), no_std)]
pub mod constants;
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.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/>.
+
#[macro_export]
macro_rules! impl_common_runtime_apis {
(
@@ -331,30 +347,30 @@
.iter()
.filter_map(|(res_id)| Some(RmrkResourceInfo {
id: res_id.0,
- pending: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::PendingResourceAccept).unwrap(),
- pending_removal: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::PendingResourceRemoval).unwrap(),
- resource: match RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::ResourceType).unwrap() {
+ pending: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::PendingResourceAccept).ok()?,
+ pending_removal: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::PendingResourceRemoval).ok()?,
+ resource: match RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::ResourceType).ok()? {
ResourceType::Basic => RmrkResourceTypes::Basic(RmrkBasicResource {
- src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).unwrap(),
- metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).unwrap(),
- license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).unwrap(),
- thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).unwrap(),
+ src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).ok()?,
+ metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).ok()?,
+ license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).ok()?,
+ thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).ok()?,
}),
ResourceType::Composable => RmrkResourceTypes::Composable(RmrkComposableResource {
- parts: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Parts).unwrap(),
- base: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Base).unwrap(),
- src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).unwrap(),
- metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).unwrap(),
- license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).unwrap(),
- thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).unwrap(),
+ parts: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Parts).ok()?,
+ base: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Base).ok()?,
+ src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).ok()?,
+ metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).ok()?,
+ license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).ok()?,
+ thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).ok()?,
}),
ResourceType::Slot => RmrkResourceTypes::Slot(RmrkSlotResource {
- base: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Base).unwrap(),
- src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).unwrap(),
- metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).unwrap(),
- slot: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Slot).unwrap(),
- license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).unwrap(),
- thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).unwrap(),
+ base: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Base).ok()?,
+ src: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Src).ok()?,
+ metadata: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Metadata).ok()?,
+ slot: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Slot).ok()?,
+ license: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::License).ok()?,
+ thumb: RmrkCore::get_nft_property_decoded(res_collection_id, *res_id, RmrkProperty::Thumb).ok()?,
}),
},
}))
@@ -447,12 +463,10 @@
let theme_names = collection.collection_tokens()
.iter()
.filter_map(|token_id| {
- let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).unwrap();
+ let nft_type = RmrkCore::get_nft_type(collection_id, *token_id).ok()?;
match nft_type {
- Theme => Some(
- RmrkCore::get_nft_property_decoded(collection_id, *token_id, RmrkProperty::ThemeName).unwrap()
- ),
+ Theme => RmrkCore::get_nft_property_decoded(collection_id, *token_id, RmrkProperty::ThemeName).ok(),
_ => None
}
})
@@ -829,6 +843,7 @@
list_benchmark!(list, extra, pallet_fungible, Fungible);
list_benchmark!(list, extra, pallet_refungible, Refungible);
list_benchmark!(list, extra, pallet_nonfungible, Nonfungible);
+ list_benchmark!(list, extra, pallet_proxy_rmrk_core, RmrkCore);
// list_benchmark!(list, extra, pallet_evm_coder_substrate, EvmCoderSubstrate);
let storage_info = AllPalletsReversedWithSystemFirst::storage_info();
@@ -872,6 +887,7 @@
add_benchmark!(params, batches, pallet_fungible, Fungible);
add_benchmark!(params, batches, pallet_refungible, Refungible);
add_benchmark!(params, batches, pallet_nonfungible, Nonfungible);
+ add_benchmark!(params, batches, pallet_proxy_rmrk_core, RmrkCore);
// add_benchmark!(params, batches, pallet_evm_coder_substrate, EvmCoderSubstrate);
if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
runtime/common/src/types.rsdiffbeforeafterboth--- a/runtime/common/src/types.rs
+++ b/runtime/common/src/types.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_runtime::{
traits::{Verify, IdentifyAccount, BlakeTwo256},
generic, MultiSignature,
runtime/opal/src/lib.rsdiffbeforeafterboth--- a/runtime/opal/src/lib.rs
+++ b/runtime/opal/src/lib.rs
@@ -919,6 +919,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}
runtime/quartz/src/lib.rsdiffbeforeafterboth--- a/runtime/quartz/src/lib.rs
+++ b/runtime/quartz/src/lib.rs
@@ -918,6 +918,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}
runtime/unique/src/lib.rsdiffbeforeafterboth--- a/runtime/unique/src/lib.rs
+++ b/runtime/unique/src/lib.rs
@@ -917,6 +917,7 @@
}
impl pallet_proxy_rmrk_core::Config for Runtime {
+ type WeightInfo = pallet_proxy_rmrk_core::weights::SubstrateWeight<Self>;
type Event = Event;
}
tests/src/addCollectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/addCollectionAdmin.test.ts
+++ b/tests/src/addCollectionAdmin.test.ts
@@ -40,8 +40,10 @@
expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
});
});
+});
- it('Add admin using added collection admin.', async () => {
+describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
+ it("Not owner can't add collection admin.", async () => {
await usingApi(async (api, privateKeyWrapper) => {
const collectionId = await createCollectionExpectSuccess();
const alice = privateKeyWrapper('//Alice');
@@ -51,38 +53,43 @@
const collection = await queryCollectionExpectSuccess(api, collectionId);
expect(collection.owner.toString()).to.be.equal(alice.address);
- const changeAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
- await submitTransactionAsync(alice, changeAdminTx);
-
const adminListAfterAddAdmin = await getAdminList(api, collectionId);
- expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
+ expect(adminListAfterAddAdmin).to.be.not.deep.contains(normalizeAccountId(bob.address));
const changeAdminTxCharlie = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(charlie.address));
- await submitTransactionAsync(bob, changeAdminTxCharlie);
+ await expect(submitTransactionAsync(bob, changeAdminTxCharlie)).to.be.rejected;
+
const adminListAfterAddNewAdmin = await getAdminList(api, collectionId);
- expect(adminListAfterAddNewAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
- expect(adminListAfterAddNewAdmin).to.be.deep.contains(normalizeAccountId(charlie.address));
+ expect(adminListAfterAddNewAdmin).to.be.not.deep.contains(normalizeAccountId(bob.address));
+ expect(adminListAfterAddNewAdmin).to.be.not.deep.contains(normalizeAccountId(charlie.address));
});
});
-});
-describe('Negative Integration Test addCollectionAdmin(collection_id, new_admin_id):', () => {
- it("Not owner can't add collection admin.", async () => {
+ it("Admin can't add collection admin.", async () => {
await usingApi(async (api, privateKeyWrapper) => {
const collectionId = await createCollectionExpectSuccess();
const alice = privateKeyWrapper('//Alice');
- const nonOwner = privateKeyWrapper('//Bob_stash');
+ const bob = privateKeyWrapper('//Bob');
+ const charlie = privateKeyWrapper('//CHARLIE');
+
+ const collection = await queryCollectionExpectSuccess(api, collectionId);
+ expect(collection.owner.toString()).to.be.equal(alice.address);
- const changeAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(alice.address));
- await expect(submitTransactionExpectFailAsync(nonOwner, changeAdminTx)).to.be.rejected;
+ const changeAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
+ await submitTransactionAsync(alice, changeAdminTx);
const adminListAfterAddAdmin = await getAdminList(api, collectionId);
- expect(adminListAfterAddAdmin).not.to.be.deep.contains(normalizeAccountId(alice.address));
+ expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
- // Verifying that nothing bad happened (network is live, new collections can be created, etc.)
- await createCollectionExpectSuccess();
+ const changeAdminTxCharlie = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(charlie.address));
+ await expect(submitTransactionAsync(bob, changeAdminTxCharlie)).to.be.rejected;
+
+ const adminListAfterAddNewAdmin = await getAdminList(api, collectionId);
+ expect(adminListAfterAddNewAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
+ expect(adminListAfterAddNewAdmin).to.be.not.deep.contains(normalizeAccountId(charlie.address));
});
});
+
it("Can't add collection admin of not existing collection.", async () => {
await usingApi(async (api, privateKeyWrapper) => {
// tslint:disable-next-line: no-bitwise
tests/src/eth/allowlist.test.tsdiffbeforeafterboth--- a/tests/src/eth/allowlist.test.ts
+++ b/tests/src/eth/allowlist.test.ts
@@ -18,8 +18,8 @@
import {contractHelpers, createEthAccountWithBalance, deployFlipper, itWeb3} from './util/helpers';
describe('EVM allowlist', () => {
- itWeb3('Contract allowlist can be toggled', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Contract allowlist can be toggled', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
@@ -36,10 +36,10 @@
expect(await helpers.methods.allowlistEnabled(flipper.options.address).call()).to.be.false;
});
- itWeb3('Non-allowlisted user can\'t call contract with allowlist enabled', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Non-allowlisted user can\'t call contract with allowlist enabled', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const helpers = contractHelpers(web3, owner);
tests/src/eth/base.test.tsdiffbeforeafterboth--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -32,16 +32,16 @@
import Web3 from 'web3';
describe('Contract calls', () => {
- itWeb3('Call of simple contract fee is less than 0.2 UNQ', async ({web3, api}) => {
- const deployer = await createEthAccountWithBalance(api, web3);
+ itWeb3('Call of simple contract fee is less than 0.2 UNQ', async ({web3, api, privateKeyWrapper}) => {
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, deployer);
const cost = await recordEthFee(api, deployer, () => flipper.methods.flip().send({from: deployer}));
expect(cost < BigInt(0.2 * Number(UNIQUE))).to.be.true;
});
- itWeb3('Balance transfer fee is less than 0.2 UNQ', async ({web3, api}) => {
- const userA = await createEthAccountWithBalance(api, web3);
+ itWeb3('Balance transfer fee is less than 0.2 UNQ', async ({web3, api, privateKeyWrapper}) => {
+ const userA = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const userB = createEthAccount(web3);
const cost = await recordEthFee(api, userA, () => web3.eth.sendTransaction({from: userA, to: userB, value: '1000000', ...GAS_ARGS}));
@@ -50,7 +50,7 @@
});
itWeb3('NFT transfer is close to 0.15 UNQ', async ({web3, api, privateKeyWrapper}) => {
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const alice = privateKeyWrapper('//Alice');
tests/src/eth/collectionProperties.test.tsdiffbeforeafterboth--- a/tests/src/eth/collectionProperties.test.ts
+++ b/tests/src/eth/collectionProperties.test.ts
@@ -7,7 +7,7 @@
describe('EVM collection properties', () => {
itWeb3('Can be set', async({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await addCollectionAdminExpectSuccess(alice, collection, {Ethereum: caller});
@@ -22,7 +22,7 @@
});
itWeb3('Can be deleted', async({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await executeTransaction(api, alice, api.tx.unique.setCollectionProperties(collection, [{key: 'testKey', value: 'testValue'}]));
tests/src/eth/contractSponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/contractSponsoring.test.ts
+++ b/tests/src/eth/contractSponsoring.test.ts
@@ -44,8 +44,8 @@
import {evmToAddress} from '@polkadot/util-crypto';
describe('Sponsoring EVM contracts', () => {
- itWeb3('Sponsoring can be set by the address that has deployed the contract', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Sponsoring can be set by the address that has deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
@@ -53,9 +53,9 @@
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.true;
});
- itWeb3('Sponsoring cannot be set by the address that did not deployed the contract', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
- const notOwner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Sponsoring cannot be set by the address that did not deployed the contract', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const notOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.sponsoringEnabled(flipper.options.address).call()).to.be.false;
@@ -66,8 +66,8 @@
itWeb3('In generous mode, non-allowlisted user transaction will be sponsored', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
@@ -94,7 +94,7 @@
itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should decrease (allowlisted)', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const caller = createEthAccount(web3);
const flipper = await deployFlipper(web3, owner);
@@ -124,7 +124,7 @@
itWeb3('Sponsoring is set, an address that has no UNQ can send a transaction and it works. Sponsor balance should not decrease (non-allowlisted)', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const caller = createEthAccount(web3);
const flipper = await deployFlipper(web3, owner);
@@ -152,8 +152,8 @@
itWeb3('Sponsoring is set, an address that has UNQ can send a transaction and it works. User balance should not change', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const originalCallerBalance = await web3.eth.getBalance(caller);
const flipper = await deployFlipper(web3, owner);
@@ -181,8 +181,8 @@
itWeb3('Sponsoring is limited, with setContractRateLimit. The limitation is working if transactions are sent more often, the sender pays the commission.', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const originalCallerBalance = await web3.eth.getBalance(caller);
const flipper = await deployFlipper(web3, owner);
@@ -214,30 +214,31 @@
});
// TODO: Find a way to calculate default rate limit
- itWeb3('Default rate limit equals 7200', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Default rate limit equals 7200', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
const helpers = contractHelpers(web3, owner);
expect(await helpers.methods.getSponsoringRateLimit(flipper.options.address).call()).to.be.equals('7200');
});
- itWeb3('Sponsoring collection from evm address via access list', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Sponsoring collection from evm address via access list', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelpers = evmCollectionHelpers(web3, owner);
let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = await createEthAccountWithBalance(api, web3);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
result = await collectionEvm.methods.setCollectionSponsor(sponsor).send({from: owner});
let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
- expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
await collectionEvm.methods.confirmCollectionSponsorship().send({from: sponsor});
collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
expect(collectionSub.sponsorship.isConfirmed).to.be.true;
- expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
const user = createEthAccount(web3);
const nextTokenId = await collectionEvm.methods.nextTokenId().call();
@@ -289,23 +290,24 @@
}
});
- itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Check that transaction via EVM spend money from sponsor address', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelpers = evmCollectionHelpers(web3, owner);
let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = await createEthAccountWithBalance(api, web3);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
+ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
- expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
expect(collectionSub.sponsorship.isConfirmed).to.be.true;
- expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
const user = createEthAccount(web3);
await collectionEvm.methods.addCollectionAdmin(user).send();
tests/src/eth/createCollection.test.tsdiffbeforeafterboth--- a/tests/src/eth/createCollection.test.ts
+++ b/tests/src/eth/createCollection.test.ts
@@ -28,67 +28,68 @@
} from './util/helpers';
describe('Create collection from EVM', () => {
- itWeb3('Create collection', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
- const collectionHelper = evmCollectionHelpers(web3, owner);
- const collectionName = 'CollectionEVM';
- const description = 'Some description';
- const tokenPrefix = 'token prefix';
+ // itWeb3('Create collection', async ({api, web3, privateKeyWrapper}) => {
+ // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ // const collectionHelper = evmCollectionHelpers(web3, owner);
+ // const collectionName = 'CollectionEVM';
+ // const description = 'Some description';
+ // const tokenPrefix = 'token prefix';
- const collectionCountBefore = await getCreatedCollectionCount(api);
- const result = await collectionHelper.methods
- .createNonfungibleCollection(collectionName, description, tokenPrefix)
- .send();
- const collectionCountAfter = await getCreatedCollectionCount(api);
+ // const collectionCountBefore = await getCreatedCollectionCount(api);
+ // const result = await collectionHelper.methods
+ // .createNonfungibleCollection(collectionName, description, tokenPrefix)
+ // .send();
+ // const collectionCountAfter = await getCreatedCollectionCount(api);
- const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
- expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
- expect(collectionId).to.be.eq(collectionCountAfter);
- expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
- expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
- expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
- });
+ // const {collectionId, collection} = await getCollectionAddressFromResult(api, result);
+ // expect(collectionCountAfter - collectionCountBefore).to.be.eq(1);
+ // expect(collectionId).to.be.eq(collectionCountAfter);
+ // expect(collection.name.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(collectionName);
+ // expect(collection.description.map(v => String.fromCharCode(v.toNumber())).join('')).to.be.eq(description);
+ // expect(collection.tokenPrefix.toHuman()).to.be.eq(tokenPrefix);
+ // });
- itWeb3('Check collection address exist', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
- const collectionHelpers = evmCollectionHelpers(web3, owner);
+ // itWeb3('Check collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ // const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ // const collectionHelpers = evmCollectionHelpers(web3, owner);
- const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
- const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
- expect(await collectionHelpers.methods
- .isCollectionExist(expectedCollectionAddress)
- .call()).to.be.false;
+ // const expectedCollectionId = await getCreatedCollectionCount(api) + 1;
+ // const expectedCollectionAddress = collectionIdToAddress(expectedCollectionId);
+ // expect(await collectionHelpers.methods
+ // .isCollectionExist(expectedCollectionAddress)
+ // .call()).to.be.false;
- await collectionHelpers.methods
- .createNonfungibleCollection('A', 'A', 'A')
- .send();
+ // await collectionHelpers.methods
+ // .createNonfungibleCollection('A', 'A', 'A')
+ // .send();
- expect(await collectionHelpers.methods
- .isCollectionExist(expectedCollectionAddress)
- .call()).to.be.true;
- });
+ // expect(await collectionHelpers.methods
+ // .isCollectionExist(expectedCollectionAddress)
+ // .call()).to.be.true;
+ // });
- itWeb3('Set sponsorship', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Set sponsorship', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelpers = evmCollectionHelpers(web3, owner);
let result = await collectionHelpers.methods.createNonfungibleCollection('Sponsor collection', '1', '1').send();
const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
- const sponsor = await createEthAccountWithBalance(api, web3);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionEvm = evmCollection(web3, owner, collectionIdAddress);
result = await collectionEvm.methods.setCollectionSponsor(sponsor).send();
let collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
expect(collectionSub.sponsorship.isUnconfirmed).to.be.true;
- expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format;
+ expect(collectionSub.sponsorship.asUnconfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
await expect(collectionEvm.methods.confirmCollectionSponsorship().call()).to.be.rejectedWith('Caller is not set as sponsor');
const sponsorCollection = evmCollection(web3, sponsor, collectionIdAddress);
await sponsorCollection.methods.confirmCollectionSponsorship().send();
collectionSub = (await getDetailedCollectionInfo(api, collectionId))!;
expect(collectionSub.sponsorship.isConfirmed).to.be.true;
- expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor));
+ expect(collectionSub.sponsorship.asConfirmed.toHuman()).to.be.eq(evmToAddress(sponsor, Number(ss58Format)));
});
- itWeb3('Set limits', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelpers = evmCollectionHelpers(web3, owner);
const result = await collectionHelpers.methods.createNonfungibleCollection('Const collection', '5', '5').send();
const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
@@ -127,8 +128,8 @@
expect(collectionSub.limits.transfersEnabled.toHuman()).to.be.eq(limits.transfersEnabled);
});
- itWeb3('Collection address exist', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Collection address exist', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionAddressForNonexistentCollection = '0x17C4E6453CC49AAAAEACA894E6D9683E00112233';
const collectionHelpers = evmCollectionHelpers(web3, owner);
expect(await collectionHelpers.methods
@@ -144,8 +145,8 @@
});
describe('(!negative tests!) Create collection from EVM', () => {
- itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('(!negative test!) Create collection (bad lengths)', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const helper = evmCollectionHelpers(web3, owner);
{
const MAX_NAME_LENGHT = 64;
@@ -190,8 +191,8 @@
.call()).to.be.rejectedWith('NotSufficientFounds');
});
- itWeb3('(!negative test!) Check owner', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('(!negative test!) Check owner', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const notOwner = await createEthAccount(web3);
const collectionHelpers = evmCollectionHelpers(web3, owner);
const result = await collectionHelpers.methods.createNonfungibleCollection('A', 'A', 'A').send();
@@ -199,7 +200,7 @@
const contractEvmFromNotOwner = evmCollection(web3, notOwner, collectionIdAddress);
const EXPECTED_ERROR = 'NoPermission';
{
- const sponsor = await createEthAccountWithBalance(api, web3);
+ const sponsor = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await expect(contractEvmFromNotOwner.methods
.setCollectionSponsor(sponsor)
.call()).to.be.rejectedWith(EXPECTED_ERROR);
@@ -216,8 +217,8 @@
}
});
- itWeb3('(!negative test!) Set limits', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('(!negative test!) Set limits', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelpers = evmCollectionHelpers(web3, owner);
const result = await collectionHelpers.methods.createNonfungibleCollection('Schema collection', 'A', 'A').send();
const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
tests/src/eth/crossTransfer.test.tsdiffbeforeafterboth--- a/tests/src/eth/crossTransfer.test.ts
+++ b/tests/src/eth/crossTransfer.test.ts
@@ -48,8 +48,8 @@
});
const alice = privateKeyWrapper('//Alice');
const bob = privateKeyWrapper('//Bob');
- const bobProxy = await createEthAccountWithBalance(api, web3);
- const aliceProxy = await createEthAccountWithBalance(api, web3);
+ const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, alice.address);
await transferExpectSuccess(collection, 0, alice, {Ethereum: aliceProxy} , 200, 'Fungible');
@@ -85,8 +85,8 @@
const alice = privateKeyWrapper('//Alice');
const bob = privateKeyWrapper('//Bob');
const charlie = privateKeyWrapper('//Charlie');
- const bobProxy = await createEthAccountWithBalance(api, web3);
- const aliceProxy = await createEthAccountWithBalance(api, web3);
+ const bobProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const aliceProxy = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
await transferExpectSuccess(collection, tokenId, alice, {Ethereum: aliceProxy} , 1, 'NFT');
const address = collectionIdToAddress(collection);
tests/src/eth/fungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/fungible.test.ts
+++ b/tests/src/eth/fungible.test.ts
@@ -27,7 +27,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});
@@ -45,7 +45,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: caller});
@@ -65,7 +65,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
@@ -208,7 +208,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const spender = createEthAccount(web3);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
@@ -226,8 +226,8 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const spender = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
@@ -246,7 +246,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
tests/src/eth/helpersSmoke.test.tsdiffbeforeafterboth--- a/tests/src/eth/helpersSmoke.test.ts
+++ b/tests/src/eth/helpersSmoke.test.ts
@@ -18,16 +18,16 @@
import {createEthAccountWithBalance, deployFlipper, itWeb3, contractHelpers} from './util/helpers';
describe('Helpers sanity check', () => {
- itWeb3('Contract owner is recorded', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Contract owner is recorded', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
expect(await contractHelpers(web3, owner).methods.contractOwner(flipper.options.address).call()).to.be.equal(owner);
});
- itWeb3('Flipper is working', async ({api, web3}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Flipper is working', async ({api, web3, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, owner);
expect(await flipper.methods.getValue().call()).to.be.false;
tests/src/eth/marketplace/marketplace.test.tsdiffbeforeafterboth--- a/tests/src/eth/marketplace/marketplace.test.ts
+++ b/tests/src/eth/marketplace/marketplace.test.ts
@@ -39,7 +39,7 @@
describe('Matcher contract usage', () => {
itWeb3('With UNQ', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const matcherOwner = await createEthAccountWithBalance(api, web3);
+ const matcherOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
@@ -100,8 +100,8 @@
itWeb3('With escrow', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const matcherOwner = await createEthAccountWithBalance(api, web3);
- const escrow = await createEthAccountWithBalance(api, web3);
+ const matcherOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const escrow = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
@@ -171,7 +171,7 @@
itWeb3('Sell tokens from substrate user via EVM contract', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const matcherOwner = await createEthAccountWithBalance(api, web3);
+ const matcherOwner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const matcherContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/MarketPlace.abi`)).toString()), undefined, {
from: matcherOwner,
...GAS_ARGS,
tests/src/eth/migration.test.tsdiffbeforeafterboth--- a/tests/src/eth/migration.test.ts
+++ b/tests/src/eth/migration.test.ts
@@ -54,7 +54,7 @@
];
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.begin(ADDRESS) as any));
await submitTransactionAsync(alice, api.tx.sudo.sudo(api.tx.evmMigration.setData(ADDRESS, DATA as any) as any));
tests/src/eth/nonFungible.test.tsdiffbeforeafterboth--- a/tests/src/eth/nonFungible.test.ts
+++ b/tests/src/eth/nonFungible.test.ts
@@ -26,7 +26,7 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
@@ -43,7 +43,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum:caller});
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
@@ -61,7 +61,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
const address = collectionIdToAddress(collection);
@@ -73,8 +73,8 @@
});
describe('NFT: Plain calls', () => {
- itWeb3('Can perform mint()', async ({web3, api}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const helper = evmCollectionHelpers(web3, owner);
let result = await helper.methods.createNonfungibleCollection('Mint collection', '6', '6').send();
const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result);
@@ -119,7 +119,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: caller});
await submitTransactionAsync(alice, changeAdminTx);
const receiver = createEthAccount(web3);
@@ -182,7 +182,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
@@ -341,7 +341,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const spender = createEthAccount(web3);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
@@ -359,8 +359,8 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const spender = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const spender = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
@@ -379,7 +379,7 @@
});
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
@@ -541,12 +541,12 @@
});
describe('Common metadata', () => {
- itWeb3('Returns collection name', async ({api, web3}) => {
+ itWeb3('Returns collection name', async ({api, web3, privateKeyWrapper}) => {
const collection = await createCollectionExpectSuccess({
name: 'token name',
mode: {type: 'NFT'},
});
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const address = collectionIdToAddress(collection);
const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
@@ -555,12 +555,12 @@
expect(name).to.equal('token name');
});
- itWeb3('Returns symbol name', async ({api, web3}) => {
+ itWeb3('Returns symbol name', async ({api, web3, privateKeyWrapper}) => {
const collection = await createCollectionExpectSuccess({
tokenPrefix: 'TOK',
mode: {type: 'NFT'},
});
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const address = collectionIdToAddress(collection);
const contract = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
tests/src/eth/payable.test.tsdiffbeforeafterboth--- a/tests/src/eth/payable.test.ts
+++ b/tests/src/eth/payable.test.ts
@@ -22,8 +22,8 @@
import {getBalanceSingle, transferBalanceExpectSuccess} from '../substrate/get-balance';
describe('EVM payable contracts', () => {
- itWeb3('Evm contract can receive wei from eth account', async ({api, web3}) => {
- const deployer = await createEthAccountWithBalance(api, web3);
+ itWeb3('Evm contract can receive wei from eth account', async ({api, web3, privateKeyWrapper}) => {
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const contract = await deployCollector(web3, deployer);
await web3.eth.sendTransaction({from: deployer, to: contract.options.address, value: '10000', ...GAS_ARGS});
@@ -32,7 +32,7 @@
});
itWeb3('Evm contract can receive wei from substrate account', async ({api, web3, privateKeyWrapper}) => {
- const deployer = await createEthAccountWithBalance(api, web3);
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const contract = await deployCollector(web3, deployer);
const alice = privateKeyWrapper('//Alice');
@@ -62,7 +62,7 @@
// We can't handle sending balance to backing storage of evm balance, because evmToAddress operation is irreversible
itWeb3('Wei sent directly to backing storage of evm contract balance is unaccounted', async({api, web3, privateKeyWrapper}) => {
- const deployer = await createEthAccountWithBalance(api, web3);
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const contract = await deployCollector(web3, deployer);
const alice = privateKeyWrapper('//Alice');
@@ -75,7 +75,7 @@
const FEE_BALANCE = 1000n * UNIQUE;
const CONTRACT_BALANCE = 1n * UNIQUE;
- const deployer = await createEthAccountWithBalance(api, web3);
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const contract = await deployCollector(web3, deployer);
const alice = privateKeyWrapper('//Alice');
tests/src/eth/proxy/fungibleProxy.test.tsdiffbeforeafterboth--- a/tests/src/eth/proxy/fungibleProxy.test.ts
+++ b/tests/src/eth/proxy/fungibleProxy.test.ts
@@ -21,10 +21,11 @@
import {ApiPromise} from '@polkadot/api';
import Web3 from 'web3';
import {readFile} from 'fs/promises';
+import {IKeyringPair} from '@polkadot/types/types';
-async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any) {
+async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any, privateKeyWrapper: (account: string) => IKeyringPair) {
// Proxy owner has no special privilegies, we don't need to reuse them
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueFungibleProxy.abi`)).toString()), undefined, {
from: owner,
...GAS_ARGS,
@@ -40,12 +41,12 @@
mode: {type: 'Fungible', decimalPoints: 0},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Substrate: alice.address});
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const totalSupply = await contract.methods.totalSupply().call();
expect(totalSupply).to.equal('200');
@@ -57,12 +58,12 @@
mode: {type: 'Fungible', decimalPoints: 0},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: caller});
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const balance = await contract.methods.balanceOf(caller).call();
expect(balance).to.equal('200');
@@ -76,11 +77,11 @@
mode: {type: 'Fungible', decimalPoints: 0},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const spender = createEthAccount(web3);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: contract.options.address});
{
@@ -112,8 +113,8 @@
mode: {type: 'Fungible', decimalPoints: 0},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
- const owner = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: owner});
@@ -121,7 +122,7 @@
const address = collectionIdToAddress(collection);
const evmCollection = new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS});
- const contract = await proxyWrap(api, web3, evmCollection);
+ const contract = await proxyWrap(api, web3, evmCollection, privateKeyWrapper);
await evmCollection.methods.approve(contract.options.address, 100).send({from: owner});
@@ -167,11 +168,11 @@
mode: {type: 'Fungible', decimalPoints: 0},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
- const receiver = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(fungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
await createFungibleItemExpectSuccess(alice, collection, {Value: 200n}, {Ethereum: contract.options.address});
{
tests/src/eth/proxy/nonFungibleProxy.test.tsdiffbeforeafterboth--- a/tests/src/eth/proxy/nonFungibleProxy.test.ts
+++ b/tests/src/eth/proxy/nonFungibleProxy.test.ts
@@ -22,10 +22,11 @@
import Web3 from 'web3';
import {readFile} from 'fs/promises';
import {ApiPromise} from '@polkadot/api';
+import {IKeyringPair} from '@polkadot/types/types';
-async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any) {
+async function proxyWrap(api: ApiPromise, web3: Web3, wrapped: any, privateKeyWrapper: (account: string) => IKeyringPair) {
// Proxy owner has no special privilegies, we don't need to reuse them
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const proxyContract = new web3.eth.Contract(JSON.parse((await readFile(`${__dirname}/UniqueNFTProxy.abi`)).toString()), undefined, {
from: owner,
...GAS_ARGS,
@@ -40,12 +41,12 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createItemExpectSuccess(alice, collection, 'NFT', {Substrate: alice.address});
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const totalSupply = await contract.methods.totalSupply().call();
expect(totalSupply).to.equal('1');
@@ -57,13 +58,13 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const balance = await contract.methods.balanceOf(caller).call();
expect(balance).to.equal('3');
@@ -75,11 +76,11 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: caller});
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const owner = await contract.methods.ownerOf(tokenId).call();
expect(owner).to.equal(caller);
@@ -87,18 +88,18 @@
});
describe('NFT (Via EVM proxy): Plain calls', () => {
- itWeb3('Can perform mint()', async ({web3, api}) => {
- const owner = await createEthAccountWithBalance(api, web3);
+ itWeb3('Can perform mint()', async ({web3, api, privateKeyWrapper}) => {
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collectionHelper = evmCollectionHelpers(web3, owner);
const result = await collectionHelper.methods
.createNonfungibleCollection('A', 'A', 'A')
.send();
const {collectionIdAddress} = await getCollectionAddressFromResult(api, result);
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const collectionEvmOwned = evmCollection(web3, owner, collectionIdAddress);
const collectionEvm = evmCollection(web3, caller, collectionIdAddress);
- const contract = await proxyWrap(api, web3, collectionEvm);
+ const contract = await proxyWrap(api, web3, collectionEvm, privateKeyWrapper);
await collectionEvmOwned.methods.addCollectionAdmin(contract.options.address).send();
{
@@ -135,11 +136,11 @@
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: contract.options.address});
await submitTransactionAsync(alice, changeAdminTx);
@@ -197,10 +198,10 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});
const changeAdminTx = api.tx.unique.addCollectionAdmin(collection, {Ethereum: contract.options.address});
@@ -229,11 +230,11 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const spender = createEthAccount(web3);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address), privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});
{
@@ -259,14 +260,14 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
- const owner = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const address = collectionIdToAddress(collection);
const evmCollection = new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS});
- const contract = await proxyWrap(api, web3, evmCollection);
+ const contract = await proxyWrap(api, web3, evmCollection, privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: owner});
await evmCollection.methods.approve(contract.options.address, tokenId).send({from: owner});
@@ -303,11 +304,11 @@
mode: {type: 'NFT'},
});
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const receiver = createEthAccount(web3);
const address = collectionIdToAddress(collection);
- const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}));
+ const contract = await proxyWrap(api, web3, new web3.eth.Contract(nonFungibleAbi as any, address, {from: caller, ...GAS_ARGS}), privateKeyWrapper);
const tokenId = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: contract.options.address});
{
tests/src/eth/scheduling.test.tsdiffbeforeafterboth--- a/tests/src/eth/scheduling.test.ts
+++ b/tests/src/eth/scheduling.test.ts
@@ -17,14 +17,13 @@
import {expect} from 'chai';
import {createEthAccountWithBalance, deployFlipper, GAS_ARGS, itWeb3, subToEth, transferBalanceToEth} from './util/helpers';
import {scheduleExpectSuccess, waitNewBlocks} from '../util/helpers';
-import privateKey from '../substrate/privateKey';
describe('Scheduing EVM smart contracts', () => {
- itWeb3('Successfully schedules and periodically executes an EVM contract', async ({api, web3}) => {
- const deployer = await createEthAccountWithBalance(api, web3);
+ itWeb3('Successfully schedules and periodically executes an EVM contract', async ({api, web3, privateKeyWrapper}) => {
+ const deployer = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const flipper = await deployFlipper(web3, deployer);
const initialValue = await flipper.methods.getValue().call();
- const alice = privateKey('//Alice');
+ const alice = privateKeyWrapper('//Alice');
await transferBalanceToEth(api, alice, subToEth(alice.address));
{
tests/src/eth/sponsoring.test.tsdiffbeforeafterboth--- a/tests/src/eth/sponsoring.test.ts
+++ b/tests/src/eth/sponsoring.test.ts
@@ -21,7 +21,7 @@
itWeb3('Fee is deducted from contract if sponsoring is enabled', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const caller = createEthAccount(web3);
const originalCallerBalance = await web3.eth.getBalance(caller);
expect(originalCallerBalance).to.be.equal('0');
@@ -52,8 +52,8 @@
itWeb3('...but this doesn\'t applies to payable value', async ({api, web3, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const owner = await createEthAccountWithBalance(api, web3);
- const caller = await createEthAccountWithBalance(api, web3);
+ const owner = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const originalCallerBalance = await web3.eth.getBalance(caller);
expect(originalCallerBalance).to.be.not.equal('0');
tests/src/eth/tokenProperties.test.tsdiffbeforeafterboth--- a/tests/src/eth/tokenProperties.test.ts
+++ b/tests/src/eth/tokenProperties.test.ts
@@ -7,7 +7,7 @@
describe('EVM token properties', () => {
itWeb3('Can be reconfigured', async({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
for(const [mutable,collectionAdmin, tokenOwner] of cartesian([], [false, true], [false, true], [false, true])) {
const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
await addCollectionAdminExpectSuccess(alice, collection, {Ethereum: caller});
@@ -25,7 +25,7 @@
});
itWeb3('Can be set', async({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
const token = await createItemExpectSuccess(alice, collection, 'NFT');
@@ -48,7 +48,7 @@
});
itWeb3('Can be deleted', async({web3, api, privateKeyWrapper}) => {
const alice = privateKeyWrapper('//Alice');
- const caller = await createEthAccountWithBalance(api, web3);
+ const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper);
const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
const token = await createItemExpectSuccess(alice, collection, 'NFT');
tests/src/eth/util/helpers.tsdiffbeforeafterboth--- a/tests/src/eth/util/helpers.ts
+++ b/tests/src/eth/util/helpers.ts
@@ -112,8 +112,8 @@
return account.address;
}
-export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3) {
- const alice = privateKey('//Alice');
+export async function createEthAccountWithBalance(api: ApiPromise, web3: Web3, privateKeyWrapper: (account: string) => IKeyringPair) {
+ const alice = privateKeyWrapper('//Alice');
const account = createEthAccount(web3);
await transferBalanceToEth(api, alice, account);
tests/src/removeCollectionAdmin.test.tsdiffbeforeafterboth--- a/tests/src/removeCollectionAdmin.test.ts
+++ b/tests/src/removeCollectionAdmin.test.ts
@@ -46,33 +46,6 @@
});
});
- it('Remove collection admin by admin.', async () => {
- await usingApi(async (api, privateKeyWrapper) => {
- const collectionId = await createCollectionExpectSuccess();
- const alice = privateKeyWrapper('//Alice');
- const bob = privateKeyWrapper('//Bob');
- const charlie = privateKeyWrapper('//Charlie');
- const collection = await queryCollectionExpectSuccess(api, collectionId);
- expect(collection.owner.toString()).to.be.eq(alice.address);
- // first - add collection admin Bob
- const addAdminTx = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(bob.address));
- await submitTransactionAsync(alice, addAdminTx);
-
- const addAdminTx2 = api.tx.unique.addCollectionAdmin(collectionId, normalizeAccountId(charlie.address));
- await submitTransactionAsync(alice, addAdminTx2);
-
- const adminListAfterAddAdmin = await getAdminList(api, collectionId);
- expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
-
- // then remove bob from admins of collection
- const removeAdminTx = api.tx.unique.removeCollectionAdmin(collectionId, normalizeAccountId(bob.address));
- await submitTransactionAsync(charlie, removeAdminTx);
-
- const adminListAfterRemoveAdmin = await getAdminList(api, collectionId);
- expect(adminListAfterRemoveAdmin).not.to.be.deep.contains(normalizeAccountId(bob.address));
- });
- });
-
it('Remove admin from collection that has no admins', async () => {
await usingApi(async (api, privateKeyWrapper) => {
const alice = privateKeyWrapper('//Alice');
@@ -120,7 +93,7 @@
});
});
- it('Regular user Can\'t remove collection admin', async () => {
+ it('Regular user can\'t remove collection admin', async () => {
await usingApi(async (api, privateKeyWrapper) => {
const collectionId = await createCollectionExpectSuccess();
const alice = privateKeyWrapper('//Alice');
@@ -137,4 +110,23 @@
await createCollectionExpectSuccess();
});
});
+
+ it('Admin can\'t remove collection admin.', async () => {
+ await usingApi(async (api, privateKeyWrapper) => {
+ const collectionId = await createCollectionExpectSuccess();
+ const alice = privateKeyWrapper('//Alice');
+ const bob = privateKeyWrapper('//Bob');
+ const charlie = privateKeyWrapper('//Charlie');
+
+ const adminListAfterAddAdmin = await getAdminList(api, collectionId);
+ expect(adminListAfterAddAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
+
+ const removeAdminTx = api.tx.unique.removeCollectionAdmin(collectionId, normalizeAccountId(bob.address));
+ await expect(submitTransactionAsync(charlie, removeAdminTx)).to.be.rejected;
+
+ const adminListAfterRemoveAdmin = await getAdminList(api, collectionId);
+ expect(adminListAfterRemoveAdmin).to.be.deep.contains(normalizeAccountId(bob.address));
+ expect(adminListAfterRemoveAdmin).to.be.deep.contains(normalizeAccountId(charlie.address));
+ });
+ });
});
tests/src/rmrk/rmrk.test.tsdiffbeforeafterboth--- a/tests/src/rmrk/rmrk.test.ts
+++ b/tests/src/rmrk/rmrk.test.ts
@@ -49,8 +49,8 @@
describe('RMRK External Integration Test', () => {
before(async () => {
- await usingApi(async () => {
- alice = privateKey('//Alice');
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
});
});
@@ -75,9 +75,9 @@
let rmrkNftId: number;
before(async () => {
- await usingApi(async api => {
- alice = privateKey('//Alice');
- bob = privateKey('//Bob');
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
+ bob = privateKeyWrapper('//Bob');
const collectionIds = await createRmrkCollection(api, alice);
uniqueCollectionId = collectionIds.uniqueId;
@@ -210,9 +210,9 @@
let nftId: number;
before(async () => {
- await usingApi(async () => {
- alice = privateKey('//Alice');
- bob = privateKey('//Bob');
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
+ bob = privateKeyWrapper('//Bob');
collectionId = await createCollectionExpectSuccess({mode: {type: 'NFT'}});
nftId = await createItemExpectSuccess(alice, collectionId, 'NFT');
tests/src/rpc.load.tsdiffbeforeafterboth--- a/tests/src/rpc.load.ts
+++ b/tests/src/rpc.load.ts
@@ -59,8 +59,7 @@
const deployer = await findUnusedAddress(api, privateKeyWrapper);
// Transfer balance to it
- const keyring = new Keyring({type: 'sr25519'});
- const alice = keyring.addFromUri('//Alice');
+ const alice = privateKeyWrapper('//Alice');
const amount = BigInt(endowment) + 10n**15n;
const tx = api.tx.balances.transfer(deployer.address, amount);
await submitTransactionAsync(alice, tx);
tests/src/scheduler.test.tsdiffbeforeafterboth--- a/tests/src/scheduler.test.ts
+++ b/tests/src/scheduler.test.ts
@@ -16,7 +16,6 @@
import chai, {expect} from 'chai';
import chaiAsPromised from 'chai-as-promised';
-import privateKey from './substrate/privateKey';
import {
default as usingApi,
submitTransactionAsync,
@@ -52,9 +51,9 @@
let scheduledIdSlider: number;
before(async() => {
- await usingApi(async () => {
- alice = privateKey('//Alice');
- bob = privateKey('//Bob');
+ await usingApi(async (api, privateKeyWrapper) => {
+ alice = privateKeyWrapper('//Alice');
+ bob = privateKeyWrapper('//Bob');
});
scheduledIdBase = '0x' + '0'.repeat(31);