diff --git a/.gitignore b/.gitignore index 2e690ea0d6..f9e07b8cd9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ einstein_key_file /.idea/ /.cargo/ +/.vscode/ tests/.vscode cumulus-parachain/ diff --git a/README.md b/README.md index 99345ed89e..b29f00fea0 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ so that we can keep the builds stable. 1. Install Rust: ```bash -sudo apt-get install git curl libssl-dev llvm pkg-config libclang-dev clang make +sudo apt-get install git curl libssl-dev llvm pkg-config libclang-dev clang make cmake curl https://sh.rustup.rs -sSf | sh ``` @@ -83,7 +83,8 @@ Note: checkout this project and all related projects (see below) in the sibling ### Polkadot launch utility ``` -git clone https://github.com/paritytech/polkadot-launch +git clone https://github.com/UniqueNetwork/polkadot-launch.git +git checkout feature/runtime-upgrade-testing ``` ### Build relay diff --git a/pallets/common/src/lib.rs b/pallets/common/src/lib.rs index 09fb3cd365..668f6dbd15 100644 --- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -1258,6 +1258,10 @@ pub trait CommonWeightInfo { } } +pub trait RefungibleExtensionsWeightInfo { + fn repartition() -> Weight; +} + pub trait CommonCollectionOperations { fn create_item( &self, @@ -1307,12 +1311,14 @@ pub trait CommonCollectionOperations { sender: T::CrossAccountId, token_id: TokenId, property: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn delete_token_properties( &self, sender: T::CrossAccountId, token_id: TokenId, property_keys: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn set_token_property_permissions( &self, @@ -1357,7 +1363,7 @@ pub trait CommonCollectionOperations { sender: T::CrossAccountId, from: (CollectionId, TokenId), under: TokenId, - budget: &dyn Budget, + nesting_budget: &dyn Budget, ) -> DispatchResult; fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId)); @@ -1384,6 +1390,19 @@ pub trait CommonCollectionOperations { spender: T::CrossAccountId, token: TokenId, ) -> u128; + fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions>; +} + +pub trait RefungibleExtensions +where + T: Config, +{ + fn repartition( + &self, + owner: &T::CrossAccountId, + token: TokenId, + amount: u128, + ) -> DispatchResultWithPostInfo; } // Flexible enough for implementing CommonCollectionOperations diff --git a/pallets/fungible/src/common.rs b/pallets/fungible/src/common.rs index 9feac2506c..e9a93813f6 100644 --- a/pallets/fungible/src/common.rs +++ b/pallets/fungible/src/common.rs @@ -18,7 +18,7 @@ use core::marker::PhantomData; use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get}; use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData}; -use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight}; +use pallet_common::{CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight}; use pallet_structure::Error as StructureError; use sp_runtime::ArithmeticError; use sp_std::{vec::Vec, vec}; @@ -298,6 +298,7 @@ impl CommonCollectionOperations for FungibleHandle { _sender: T::CrossAccountId, _token_id: TokenId, _property: Vec, + _nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { fail!(>::SettingPropertiesNotAllowed) } @@ -315,6 +316,7 @@ impl CommonCollectionOperations for FungibleHandle { _sender: T::CrossAccountId, _token_id: TokenId, _property_keys: Vec, + _nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { fail!(>::SettingPropertiesNotAllowed) } @@ -324,7 +326,7 @@ impl CommonCollectionOperations for FungibleHandle { _sender: ::CrossAccountId, _from: (CollectionId, TokenId), _under: TokenId, - _budget: &dyn Budget, + _nesting_budget: &dyn Budget, ) -> sp_runtime::DispatchResult { fail!(>::FungibleDisallowsNesting) } @@ -399,4 +401,8 @@ impl CommonCollectionOperations for FungibleHandle { } >::get((self.id, sender, spender)) } + + fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions> { + None + } } diff --git a/pallets/nonfungible/src/benchmarking.rs b/pallets/nonfungible/src/benchmarking.rs index ec723df788..5e8b51be2b 100644 --- a/pallets/nonfungible/src/benchmarking.rs +++ b/pallets/nonfungible/src/benchmarking.rs @@ -183,7 +183,7 @@ benchmarks! { value: property_value(), }).collect::>(); let item = create_max_item(&collection, &owner, owner.clone())?; - }: {>::set_token_properties(&collection, &owner, item, props, false)?} + }: {>::set_token_properties(&collection, &owner, item, props, false, &Unlimited)?} delete_token_properties { let b in 0..MAX_PROPERTIES_PER_ITEM; @@ -205,7 +205,7 @@ benchmarks! { value: property_value(), }).collect::>(); let item = create_max_item(&collection, &owner, owner.clone())?; - >::set_token_properties(&collection, &owner, item, props, false)?; + >::set_token_properties(&collection, &owner, item, props, false, &Unlimited)?; let to_delete = (0..b).map(|k| property_key(k as usize)).collect::>(); - }: {>::delete_token_properties(&collection, &owner, item, to_delete)?} + }: {>::delete_token_properties(&collection, &owner, item, to_delete, &Unlimited)?} } diff --git a/pallets/nonfungible/src/common.rs b/pallets/nonfungible/src/common.rs index c3326ddc19..c545903ed6 100644 --- a/pallets/nonfungible/src/common.rs +++ b/pallets/nonfungible/src/common.rs @@ -22,7 +22,8 @@ use up_data_structs::{ PropertyKeyPermission, PropertyValue, }; use pallet_common::{ - CommonCollectionOperations, CommonWeightInfo, with_weight, weights::WeightInfo as _, + CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight, + weights::WeightInfo as _, }; use sp_runtime::DispatchError; use sp_std::vec::Vec; @@ -219,11 +220,19 @@ impl CommonCollectionOperations for NonfungibleHandle { sender: T::CrossAccountId, token_id: TokenId, properties: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let weight = >::set_token_properties(properties.len() as u32); with_weight( - >::set_token_properties(self, &sender, token_id, properties, false), + >::set_token_properties( + self, + &sender, + token_id, + properties.into_iter(), + false, + nesting_budget, + ), weight, ) } @@ -233,11 +242,18 @@ impl CommonCollectionOperations for NonfungibleHandle { sender: T::CrossAccountId, token_id: TokenId, property_keys: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let weight = >::delete_token_properties(property_keys.len() as u32); with_weight( - >::delete_token_properties(self, &sender, token_id, property_keys), + >::delete_token_properties( + self, + &sender, + token_id, + property_keys.into_iter(), + nesting_budget, + ), weight, ) } @@ -367,9 +383,9 @@ impl CommonCollectionOperations for NonfungibleHandle { sender: T::CrossAccountId, from: (CollectionId, TokenId), under: TokenId, - budget: &dyn Budget, + nesting_budget: &dyn Budget, ) -> sp_runtime::DispatchResult { - >::check_nesting(self, sender, from, under, budget) + >::check_nesting(self, sender, from, under, nesting_budget) } fn nest(&self, under: TokenId, to_nest: (CollectionId, TokenId)) { @@ -467,4 +483,8 @@ impl CommonCollectionOperations for NonfungibleHandle { 0 } } + + fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions> { + None + } } diff --git a/pallets/nonfungible/src/erc.rs b/pallets/nonfungible/src/erc.rs index 54dcd9f898..5b32660a63 100644 --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -82,12 +82,16 @@ impl NonfungibleHandle { .map_err(|_| "key too long")?; let value = value.try_into().map_err(|_| "value too long")?; + let nesting_budget = self + .recorder + .weight_calls_budget(>::find_parent()); + >::set_token_property( self, &caller, TokenId(token_id), Property { key, value }, - false, + &nesting_budget, ) .map_err(dispatch_to_evm::) } @@ -99,7 +103,11 @@ impl NonfungibleHandle { .try_into() .map_err(|_| "key too long")?; - >::delete_token_property(self, &caller, TokenId(token_id), key) + let nesting_budget = self + .recorder + .weight_calls_budget(>::find_parent()); + + >::delete_token_property(self, &caller, TokenId(token_id), key, &nesting_budget) .map_err(dispatch_to_evm::) } diff --git a/pallets/nonfungible/src/lib.rs b/pallets/nonfungible/src/lib.rs index 37bb68693b..76f647326c 100644 --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -28,7 +28,8 @@ use frame_support::{ use up_data_structs::{ AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData, mapping::TokenAddressMapping, budget::Budget, Property, PropertyPermission, PropertyKey, - PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild, AuxPropertyValue, + PropertyValue, PropertyKeyPermission, Properties, PropertyScope, TrySetProperty, TokenChild, + AuxPropertyValue, }; use pallet_evm::{account::CrossAccountId, Pallet as PalletEvm}; use pallet_common::{ @@ -379,11 +380,7 @@ impl Pallet { ) -> DispatchResult { let token_data = >::get((collection.id, token)).ok_or(>::TokenNotFound)?; - ensure!( - &token_data.owner == sender - || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)), - >::NoPermission - ); + ensure!(&token_data.owner == sender, >::NoPermission); if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(sender)?; @@ -484,139 +481,169 @@ impl Pallet { }) } - pub fn set_token_property( + #[transactional] + fn modify_token_properties( collection: &NonfungibleHandle, sender: &T::CrossAccountId, token_id: TokenId, - property: Property, + properties: impl Iterator)>, is_token_create: bool, + nesting_budget: &dyn Budget, ) -> DispatchResult { - Self::check_token_change_permission( - collection, - sender, - token_id, - &property.key, - is_token_create, - )?; + let mut collection_admin_status = None; + let mut token_owner_result = None; - >::try_mutate((collection.id, token_id), |properties| { - let property = property.clone(); - properties.try_set(property.key, property.value) - }) - .map_err(>::from)?; + let mut is_collection_admin = + || *collection_admin_status.get_or_insert_with(|| collection.is_owner_or_admin(sender)); - >::deposit_event(CommonEvent::TokenPropertySet( - collection.id, - token_id, - property.key, - )); + let mut is_token_owner = || { + *token_owner_result.get_or_insert_with(|| -> Result { + let is_owned = >::check_indirectly_owned( + sender.clone(), + collection.id, + token_id, + None, + nesting_budget, + )?; + + Ok(is_owned) + }) + }; + + for (key, value) in properties { + let permission = >::property_permissions(collection.id) + .get(&key) + .cloned() + .unwrap_or_else(PropertyPermission::none); + + let is_property_exists = TokenProperties::::get((collection.id, token_id)) + .get(&key) + .is_some(); + + match permission { + PropertyPermission { mutable: false, .. } if is_property_exists => { + return Err(>::NoPermission.into()); + } + + PropertyPermission { + collection_admin, + token_owner, + .. + } => { + //TODO: investigate threats during public minting. + if is_token_create && (collection_admin || token_owner) && value.is_some() { + // Pass + } else if collection_admin && is_collection_admin() { + // Pass + } else if token_owner && is_token_owner()? { + // Pass + } else { + fail!(>::NoPermission); + } + } + } + + match value { + Some(value) => { + >::try_mutate((collection.id, token_id), |properties| { + properties.try_set(key.clone(), value) + }) + .map_err(>::from)?; + + >::deposit_event(CommonEvent::TokenPropertySet( + collection.id, + token_id, + key, + )); + } + None => { + >::try_mutate((collection.id, token_id), |properties| { + properties.remove(&key) + }) + .map_err(>::from)?; + + >::deposit_event(CommonEvent::TokenPropertyDeleted( + collection.id, + token_id, + key, + )); + } + } + } Ok(()) } - #[transactional] pub fn set_token_properties( collection: &NonfungibleHandle, sender: &T::CrossAccountId, token_id: TokenId, - properties: Vec, + properties: impl Iterator, is_token_create: bool, + nesting_budget: &dyn Budget, ) -> DispatchResult { - for property in properties { - Self::set_token_property(collection, sender, token_id, property, is_token_create)?; - } - - Ok(()) + Self::modify_token_properties( + collection, + sender, + token_id, + properties.map(|p| (p.key, Some(p.value))), + is_token_create, + nesting_budget, + ) } - pub fn delete_token_property( + pub fn set_token_property( collection: &NonfungibleHandle, sender: &T::CrossAccountId, token_id: TokenId, - property_key: PropertyKey, + property: Property, + nesting_budget: &dyn Budget, ) -> DispatchResult { - Self::check_token_change_permission(collection, sender, token_id, &property_key, false)?; + let is_token_create = false; - >::try_mutate((collection.id, token_id), |properties| { - properties.remove(&property_key) - }) - .map_err(>::from)?; - - >::deposit_event(CommonEvent::TokenPropertyDeleted( - collection.id, + Self::set_token_properties( + collection, + sender, token_id, - property_key, - )); - - Ok(()) + [property].into_iter(), + is_token_create, + nesting_budget, + ) } - fn check_token_change_permission( + pub fn delete_token_properties( collection: &NonfungibleHandle, sender: &T::CrossAccountId, token_id: TokenId, - property_key: &PropertyKey, - is_token_create: bool, + property_keys: impl Iterator, + nesting_budget: &dyn Budget, ) -> DispatchResult { - let permission = >::property_permissions(collection.id) - .get(property_key) - .cloned() - .unwrap_or_else(PropertyPermission::none); - - let token_data = >::get((collection.id, token_id)) - .ok_or(>::TokenNotFound)?; - - let check_token_owner = || -> DispatchResult { - ensure!(&token_data.owner == sender, >::NoPermission); - Ok(()) - }; - - let is_property_exists = TokenProperties::::get((collection.id, token_id)) - .get(property_key) - .is_some(); - - match permission { - PropertyPermission { mutable: false, .. } if is_property_exists => { - Err(>::NoPermission.into()) - } - - PropertyPermission { - collection_admin, - token_owner, - .. - } => { - //TODO: investigate threats during public minting. - if is_token_create && (collection_admin || token_owner) { - return Ok(()); - } - - let mut check_result = Err(>::NoPermission.into()); - - if collection_admin { - check_result = collection.check_is_owner_or_admin(sender); - } + let is_token_create = false; - if token_owner { - check_result.or_else(|_| check_token_owner()) - } else { - check_result - } - } - } + Self::modify_token_properties( + collection, + sender, + token_id, + property_keys.into_iter().map(|key| (key, None)), + is_token_create, + nesting_budget, + ) } - #[transactional] - pub fn delete_token_properties( + pub fn delete_token_property( collection: &NonfungibleHandle, sender: &T::CrossAccountId, token_id: TokenId, - property_keys: Vec, + property_key: PropertyKey, + nesting_budget: &dyn Budget, ) -> DispatchResult { - for key in property_keys { - Self::delete_token_property(collection, sender, token_id, key)?; - } - - Ok(()) + Self::delete_token_properties( + collection, + sender, + token_id, + [property_key].into_iter(), + nesting_budget, + ) } pub fn set_collection_properties( @@ -665,12 +692,7 @@ impl Pallet { let token_data = >::get((collection.id, token)).ok_or(>::TokenNotFound)?; - // TODO: require sender to be token, owner, require admins to go through transfer_from - ensure!( - &token_data.owner == from - || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)), - >::NoPermission - ); + ensure!(&token_data.owner == from, >::NoPermission); if collection.permissions.access() == AccessMode::AllowList { collection.check_allowlist(from)?; @@ -827,8 +849,9 @@ impl Pallet { collection, sender, TokenId(token), - data.properties.clone().into_inner(), + data.properties.clone().into_iter(), true, + nesting_budget, ) { return TransactionOutcome::Rollback(Err(e)); } @@ -976,8 +999,12 @@ impl Pallet { // `from`, `to` checked in [`transfer`] collection.check_allowlist(spender)?; } + + if collection.limits.owner_can_transfer() && collection.is_owner_or_admin(spender) { + return Ok(()); + } + if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) { - // TODO: should collection owner be allowed to perform this transfer? ensure!( >::check_indirectly_owned( spender.clone(), diff --git a/pallets/proxy-rmrk-core/src/lib.rs b/pallets/proxy-rmrk-core/src/lib.rs index 28f3cef8c1..99945492ed 100644 --- a/pallets/proxy-rmrk-core/src/lib.rs +++ b/pallets/proxy-rmrk-core/src/lib.rs @@ -19,7 +19,10 @@ use frame_support::{pallet_prelude::*, transactional, BoundedVec, dispatch::DispatchResult}; use frame_system::{pallet_prelude::*, ensure_signed}; use sp_runtime::{DispatchError, Permill, traits::StaticLookup}; -use sp_std::vec::Vec; +use sp_std::{ + vec::Vec, + collections::{btree_set::BTreeSet, btree_map::BTreeMap}, +}; use up_data_structs::{*, mapping::TokenAddressMapping}; use pallet_common::{ Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations, @@ -48,6 +51,12 @@ use RmrkProperty::*; pub const NESTING_BUDGET: u32 = 5; +type PendingTarget = (CollectionId, TokenId); +type PendingChild = (RmrkCollectionId, RmrkNftId); +type PendingChildrenSet = BTreeSet; + +type BasesMap = BTreeMap; + #[frame_support::pallet] pub mod pallet { use super::*; @@ -383,12 +392,14 @@ pub mod pallet { [ Self::rmrk_property(TokenType, &NftType::Regular)?, Self::rmrk_property(Transferable, &transferable)?, - Self::rmrk_property(PendingNftAccept, &false)?, + Self::rmrk_property(PendingNftAccept, &None::)?, Self::rmrk_property(RoyaltyInfo, &royalty_info)?, Self::rmrk_property(Metadata, &metadata)?, Self::rmrk_property(Equipped, &false)?, Self::rmrk_property(ResourcePriorities, &>::new())?, Self::rmrk_property(NextResourceId, &(0 as RmrkResourceId))?, + Self::rmrk_property(PendingChildren, &PendingChildrenSet::new())?, + Self::rmrk_property(AssociatedBases, &BasesMap::new())?, ] .into_iter(), ) @@ -483,11 +494,12 @@ pub mod pallet { ); ensure!( - !Self::get_nft_property_decoded( + Self::get_nft_property_decoded::>( collection_id, nft_id, RmrkProperty::PendingNftAccept - )?, + )? + .is_none(), >::NoPermission ); @@ -524,7 +536,15 @@ pub mod pallet { collection.id, nft_id, PropertyScope::Rmrk, - Self::rmrk_property(PendingNftAccept, &approval_required)?, + Self::rmrk_property::>( + PendingNftAccept, + &Some((target_collection_id, target_nft_id.into())), + )?, + )?; + + Self::insert_pending_child( + (target_collection_id, target_nft_id.into()), + (rmrk_collection_id, rmrk_nft_id), )?; } else { target_owner = T::CrossTokenAddressMapping::token_to_address( @@ -618,13 +638,23 @@ pub mod pallet { } })?; - >::set_scoped_token_property( - collection.id, + let pending_target = Self::get_nft_property_decoded::>( + collection_id, nft_id, - PropertyScope::Rmrk, - Self::rmrk_property(PendingNftAccept, &false)?, + RmrkProperty::PendingNftAccept, )?; + if let Some(pending_target) = pending_target { + Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))?; + + >::set_scoped_token_property( + collection.id, + nft_id, + PropertyScope::Rmrk, + Self::rmrk_property(PendingNftAccept, &None::)?, + )?; + } + Self::deposit_event(Event::NFTAccepted { sender, recipient: new_owner, @@ -663,14 +693,18 @@ pub mod pallet { >::NoAvailableNftId ); - ensure!( - Self::get_nft_property_decoded( - collection_id, - nft_id, - RmrkProperty::PendingNftAccept - )?, - >::CannotRejectNonPendingNft - ); + let pending_target = Self::get_nft_property_decoded::>( + collection_id, + nft_id, + RmrkProperty::PendingNftAccept, + )?; + + match pending_target { + Some(pending_target) => { + Self::remove_pending_child(pending_target, (rmrk_collection_id, rmrk_nft_id))? + } + None => return Err(>::CannotRejectNonPendingNft.into()), + } Self::destroy_nft( cross_sender, @@ -785,6 +819,12 @@ pub mod pallet { resource_id_key, ); + if let RmrkResourceTypes::Composable(resource) = resource_info.resource { + let base_id = resource.base; + + Self::remove_associated_base_id(collection_id, nft_id, base_id)?; + } + Self::deposit_event(Event::::ResourceRemovalAccepted { nft_id: rmrk_nft_id, resource_id, @@ -938,6 +978,8 @@ pub mod pallet { Self::get_typed_nft_collection(collection_id, misc::CollectionType::Regular)?; collection.check_is_external()?; + let base_id = resource.base; + let resource_id = Self::resource_add( sender, collection_id, @@ -945,6 +987,24 @@ pub mod pallet { RmrkResourceTypes::Composable(resource), )?; + >::try_mutate_token_aux_property( + collection_id, + nft_id.into(), + PropertyScope::Rmrk, + Self::rmrk_property_key(AssociatedBases)?, + |value| -> DispatchResult { + let mut bases: BasesMap = match value { + Some(value) => Self::decode_property(value)?, + None => BasesMap::new(), + }; + + *bases.entry(base_id).or_insert(0) += 1; + + *value = Some(Self::encode_property(&bases)?); + Ok(()) + }, + )?; + Self::deposit_event(Event::ResourceAdded { nft_id, resource_id, @@ -1147,6 +1207,67 @@ impl Pallet { ) } + fn insert_pending_child( + target: (CollectionId, TokenId), + child: (RmrkCollectionId, RmrkNftId), + ) -> DispatchResult { + Self::mutate_pending_child(target, |pending_children| { + pending_children.insert(child); + }) + } + + fn remove_pending_child( + target: (CollectionId, TokenId), + child: (RmrkCollectionId, RmrkNftId), + ) -> DispatchResult { + Self::mutate_pending_child(target, |pending_children| { + pending_children.remove(&child); + }) + } + + fn mutate_pending_child( + (target_collection_id, target_nft_id): (CollectionId, TokenId), + f: impl FnOnce(&mut PendingChildrenSet), + ) -> DispatchResult { + >::try_mutate_token_aux_property( + target_collection_id, + target_nft_id, + PropertyScope::Rmrk, + Self::rmrk_property_key(PendingChildren)?, + |pending_children| -> DispatchResult { + let mut map = match pending_children { + Some(map) => Self::decode_property(map)?, + None => PendingChildrenSet::new(), + }; + + f(&mut map); + + *pending_children = Some(Self::encode_property(&map)?); + + Ok(()) + }, + ) + } + + fn iterate_pending_children( + collection_id: CollectionId, + nft_id: TokenId, + ) -> Result, DispatchError> { + let property = >::token_aux_property(( + collection_id, + nft_id, + PropertyScope::Rmrk, + Self::rmrk_property_key(PendingChildren)?, + )); + + let pending_children = match property { + Some(map) => Self::decode_property(&map)?, + None => PendingChildrenSet::new(), + }; + + Ok(pending_children.into_iter()) + } + fn acquire_next_resource_id( collection_id: CollectionId, nft_id: TokenId, @@ -1223,16 +1344,15 @@ impl Pallet { let resource_id_key = Self::rmrk_property_key(ResourceId(resource_id))?; let scope = PropertyScope::Rmrk; - ensure!( - >::token_aux_property(( - collection_id, - nft_id, - scope, - resource_id_key.clone() - )) - .is_some(), - >::ResourceDoesntExist - ); + let resource = >::token_aux_property(( + collection_id, + nft_id, + scope, + resource_id_key.clone(), + )) + .ok_or(>::ResourceDoesntExist)?; + + let resource_info: RmrkResourceInfo = Self::decode_property(&resource)?; let budget = up_data_structs::budget::Value::new(NESTING_BUDGET); let topmost_owner = @@ -1246,6 +1366,12 @@ impl Pallet { PropertyScope::Rmrk, Self::rmrk_property_key(ResourceId(resource_id))?, ); + + if let RmrkResourceTypes::Composable(resource) = resource_info.resource { + let base_id = resource.base; + + Self::remove_associated_base_id(collection_id, nft_id, base_id)?; + } } else { Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| { res.pending_removal = true; @@ -1257,6 +1383,36 @@ impl Pallet { Ok(()) } + fn remove_associated_base_id( + collection_id: CollectionId, + nft_id: TokenId, + base_id: RmrkBaseId, + ) -> DispatchResult { + >::try_mutate_token_aux_property( + collection_id, + nft_id, + PropertyScope::Rmrk, + Self::rmrk_property_key(AssociatedBases)?, + |value| -> DispatchResult { + let mut bases: BasesMap = match value { + Some(value) => Self::decode_property(value)?, + None => BasesMap::new(), + }; + + let remaining = bases.get(&base_id); + + if let Some(remaining) = remaining { + if let Some(0) | None = remaining.checked_sub(1) { + bases.remove(&base_id); + } + } + + *value = Some(Self::encode_property(&bases)?); + Ok(()) + }, + ) + } + fn try_mutate_resource_info( collection_id: CollectionId, nft_id: TokenId, @@ -1526,15 +1682,13 @@ impl Pallet { Value: Decode + Default, Mapper: Fn(Key, Value) -> R, { - let key_prefix = Self::rmrk_property_key(UserProperty(b""))?; - let properties = match token_id { Some(token_id) => >::token_properties((collection_id, token_id)), None => >::collection_properties(collection_id), }; let properties = properties.into_iter().filter_map(move |(key, value)| { - let key = key.as_slice().strip_prefix(key_prefix.as_slice())?; + let key = strip_key_prefix(&key, USER_PROPERTY_PREFIX)?; let key: Key = key.to_vec().try_into().ok()?; let value: Value = value.decode().ok()?; diff --git a/pallets/proxy-rmrk-core/src/property.rs b/pallets/proxy-rmrk-core/src/property.rs index b76878320d..1d80b03d6a 100644 --- a/pallets/proxy-rmrk-core/src/property.rs +++ b/pallets/proxy-rmrk-core/src/property.rs @@ -15,9 +15,11 @@ // along with Unique Network. If not, see . use super::*; +use up_data_structs::PropertyScope; use core::convert::AsRef; -const RESOURCE_ID_PREFIX: &str = "rsid-"; +pub const RESOURCE_ID_PREFIX: &str = "rsid-"; +pub const USER_PROPERTY_PREFIX: &str = "userprop-"; pub enum RmrkProperty<'r> { Metadata, @@ -31,6 +33,8 @@ pub enum RmrkProperty<'r> { NextResourceId, ResourceId(RmrkResourceId), PendingNftAccept, + PendingChildren, + AssociatedBases, Parts, Base, Src, @@ -73,6 +77,8 @@ impl<'r> RmrkProperty<'r> { Self::NextResourceId => key!("next-resource-id"), Self::ResourceId(id) => key!(RESOURCE_ID_PREFIX, id.to_le_bytes()), Self::PendingNftAccept => key!("pending-nft-accept"), + Self::PendingChildren => key!("pending-children"), + Self::AssociatedBases => key!("assoc-bases"), Self::Parts => key!("parts"), Self::Base => key!("base"), Self::Src => key!("src"), @@ -83,7 +89,22 @@ impl<'r> RmrkProperty<'r> { Self::ZIndex => key!("z-index"), Self::ThemeName => key!("theme-name"), Self::ThemeInherit => key!("theme-inherit"), - Self::UserProperty(name) => key!("userprop-", name), + Self::UserProperty(name) => key!(USER_PROPERTY_PREFIX, name), } } } + +pub fn strip_key_prefix(key: &PropertyKey, prefix: &str) -> Option { + let key_prefix = PropertyKey::try_from(prefix.as_bytes().to_vec()).ok()?; + let key_prefix = PropertyScope::Rmrk.apply(key_prefix).ok()?; + + key.as_slice() + .strip_prefix(key_prefix.as_slice())? + .to_vec() + .try_into() + .ok() +} + +pub fn is_valid_key_prefix(key: &PropertyKey, prefix: &str) -> bool { + strip_key_prefix(key, prefix).is_some() +} diff --git a/pallets/proxy-rmrk-core/src/rpc.rs b/pallets/proxy-rmrk-core/src/rpc.rs index 6a898cf5ee..df38f71b1f 100644 --- a/pallets/proxy-rmrk-core/src/rpc.rs +++ b/pallets/proxy-rmrk-core/src/rpc.rs @@ -132,17 +132,6 @@ pub fn nft_children( Ok( pallet_nonfungible::TokenChildren::::iter_prefix((collection_id, nft_id)) .filter_map(|((child_collection, child_token), _)| { - let is_pending = >::get_nft_property_decoded( - child_collection, - child_token, - RmrkProperty::PendingNftAccept, - ) - .ok()?; - - if is_pending { - return None; - } - let rmrk_child_collection = >::rmrk_collection_id(child_collection).ok()?; @@ -151,6 +140,14 @@ pub fn nft_children( nft_id: child_token.0, }) }) + .chain( + >::iterate_pending_children(collection_id, nft_id)?.map( + |(child_collection, child_nft_id)| RmrkNftChild { + collection_id: child_collection, + nft_id: child_nft_id, + }, + ), + ) .collect(), ) } @@ -224,7 +221,11 @@ pub fn nft_resources( nft_id, PropertyScope::Rmrk, ) - .filter_map(|(_, value)| { + .filter_map(|(key, value)| { + if !is_valid_key_prefix(&key, RESOURCE_ID_PREFIX) { + return None; + } + let resource_info: RmrkResourceInfo = >::decode_property(&value).ok()?; Some(resource_info) diff --git a/pallets/proxy-rmrk-core/src/weights.rs b/pallets/proxy-rmrk-core/src/weights.rs index 7301d98988..033440cf43 100644 --- a/pallets/proxy-rmrk-core/src/weights.rs +++ b/pallets/proxy-rmrk-core/src/weights.rs @@ -3,7 +3,7 @@ //! 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-21, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-07-01, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -64,7 +64,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Common CollectionById (r:0 w:1) // Storage: RmrkCore UniqueCollectionId (r:0 w:1) fn create_collection() -> Weight { - (49_986_000 as Weight) + (49_365_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(8 as Weight)) } @@ -77,7 +77,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokensBurnt (r:0 w:1) // Storage: Common AdminAmount (r:0 w:1) fn destroy_collection() -> Weight { - (50_177_000 as Weight) + (49_903_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -85,7 +85,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Common CollectionById (r:1 w:1) // Storage: Common CollectionProperties (r:1 w:0) fn change_collection_issuer() -> Weight { - (25_949_000 as Weight) + (26_134_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -95,7 +95,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokensMinted (r:1 w:0) // Storage: Nonfungible TokensBurnt (r:1 w:0) fn lock_collection() -> Weight { - (27_183_000 as Weight) + (28_020_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -109,9 +109,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible Owned (r:0 w:1) // Storage: Nonfungible TokenAuxProperties (r:2 w:2) fn mint_nft(b: u32, ) -> Weight { - (65_512_000 as Weight) - // Standard Error: 12_000 - .saturating_add((11_525_000 as Weight).saturating_mul(b as Weight)) + (67_476_000 as Weight) + // Standard Error: 19_000 + .saturating_add((12_373_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(5 as Weight)) @@ -129,8 +129,8 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenProperties (r:0 w:1) fn burn_nft(b: u32, ) -> Weight { (0 as Weight) - // Standard Error: 1_472_000 - .saturating_add((296_221_000 as Weight).saturating_mul(b as Weight)) + // Standard Error: 1_500_000 + .saturating_add((300_520_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(6 as Weight)) @@ -146,7 +146,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenChildren (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:2) fn send() -> Weight { - (82_778_000 as Weight) + (84_023_000 as Weight) .saturating_add(T::DbWeight::get().reads(12 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -157,27 +157,29 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible AccountBalance (r:2 w:2) // Storage: Nonfungible Allowance (r:1 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) + // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenChildren (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:2) fn accept_nft() -> Weight { - (90_190_000 as Weight) - .saturating_add(T::DbWeight::get().reads(15 as Weight)) - .saturating_add(T::DbWeight::get().writes(7 as Weight)) + (98_502_000 as Weight) + .saturating_add(T::DbWeight::get().reads(16 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) // Storage: Common CollectionById (r:1 w:0) // Storage: Nonfungible TokenData (r:5 w:5) // Storage: Nonfungible TokenProperties (r:1 w:5) + // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenChildren (r:9 w:4) // Storage: Nonfungible TokensBurnt (r:1 w:1) // Storage: Nonfungible AccountBalance (r:5 w:5) // Storage: Nonfungible Allowance (r:5 w:0) // Storage: Nonfungible Owned (r:0 w:5) fn reject_nft() -> Weight { - (262_238_000 as Weight) - .saturating_add(T::DbWeight::get().reads(29 as Weight)) - .saturating_add(T::DbWeight::get().writes(25 as Weight)) + (277_017_000 as Weight) + .saturating_add(T::DbWeight::get().reads(30 as Weight)) + .saturating_add(T::DbWeight::get().writes(26 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) @@ -185,7 +187,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn set_property() -> Weight { - (55_187_000 as Weight) + (55_408_000 as Weight) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -195,7 +197,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn set_priority() -> Weight { - (54_092_000 as Weight) + (55_063_000 as Weight) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -206,7 +208,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn add_basic_resource() -> Weight { - (62_551_000 as Weight) + (64_656_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -215,11 +217,11 @@ impl WeightInfo for SubstrateWeight { // Storage: Common CollectionById (r:1 w:0) // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) - // Storage: Nonfungible TokenAuxProperties (r:1 w:1) + // Storage: Nonfungible TokenAuxProperties (r:2 w:2) fn add_composable_resource() -> Weight { - (61_778_000 as Weight) - .saturating_add(T::DbWeight::get().reads(10 as Weight)) - .saturating_add(T::DbWeight::get().writes(2 as Weight)) + (67_593_000 as Weight) + .saturating_add(T::DbWeight::get().reads(11 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) @@ -228,7 +230,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn add_slot_resource() -> Weight { - (62_700_000 as Weight) + (63_845_000 as Weight) .saturating_add(T::DbWeight::get().reads(10 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -238,7 +240,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn remove_resource() -> Weight { - (52_920_000 as Weight) + (53_414_000 as Weight) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -248,7 +250,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn accept_resource() -> Weight { - (52_450_000 as Weight) + (51_869_000 as Weight) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -258,7 +260,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn accept_resource_removal() -> Weight { - (50_901_000 as Weight) + (53_168_000 as Weight) .saturating_add(T::DbWeight::get().reads(9 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -275,7 +277,7 @@ impl WeightInfo for () { // Storage: Common CollectionById (r:0 w:1) // Storage: RmrkCore UniqueCollectionId (r:0 w:1) fn create_collection() -> Weight { - (49_986_000 as Weight) + (49_365_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) } @@ -288,7 +290,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokensBurnt (r:0 w:1) // Storage: Common AdminAmount (r:0 w:1) fn destroy_collection() -> Weight { - (50_177_000 as Weight) + (49_903_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } @@ -296,7 +298,7 @@ impl WeightInfo for () { // Storage: Common CollectionById (r:1 w:1) // Storage: Common CollectionProperties (r:1 w:0) fn change_collection_issuer() -> Weight { - (25_949_000 as Weight) + (26_134_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -306,7 +308,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokensMinted (r:1 w:0) // Storage: Nonfungible TokensBurnt (r:1 w:0) fn lock_collection() -> Weight { - (27_183_000 as Weight) + (28_020_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -320,9 +322,9 @@ impl WeightInfo for () { // Storage: Nonfungible Owned (r:0 w:1) // Storage: Nonfungible TokenAuxProperties (r:2 w:2) fn mint_nft(b: u32, ) -> Weight { - (65_512_000 as Weight) - // Standard Error: 12_000 - .saturating_add((11_525_000 as Weight).saturating_mul(b as Weight)) + (67_476_000 as Weight) + // Standard Error: 19_000 + .saturating_add((12_373_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(5 as Weight)) @@ -340,8 +342,8 @@ impl WeightInfo for () { // Storage: Nonfungible TokenProperties (r:0 w:1) fn burn_nft(b: u32, ) -> Weight { (0 as Weight) - // Standard Error: 1_472_000 - .saturating_add((296_221_000 as Weight).saturating_mul(b as Weight)) + // Standard Error: 1_500_000 + .saturating_add((300_520_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().reads((4 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) @@ -357,7 +359,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenChildren (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:2) fn send() -> Weight { - (82_778_000 as Weight) + (84_023_000 as Weight) .saturating_add(RocksDbWeight::get().reads(12 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } @@ -368,27 +370,29 @@ impl WeightInfo for () { // Storage: Nonfungible AccountBalance (r:2 w:2) // Storage: Nonfungible Allowance (r:1 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) + // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenChildren (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:2) fn accept_nft() -> Weight { - (90_190_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(15 as Weight)) - .saturating_add(RocksDbWeight::get().writes(7 as Weight)) + (98_502_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(16 as Weight)) + .saturating_add(RocksDbWeight::get().writes(8 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) // Storage: Common CollectionById (r:1 w:0) // Storage: Nonfungible TokenData (r:5 w:5) // Storage: Nonfungible TokenProperties (r:1 w:5) + // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenChildren (r:9 w:4) // Storage: Nonfungible TokensBurnt (r:1 w:1) // Storage: Nonfungible AccountBalance (r:5 w:5) // Storage: Nonfungible Allowance (r:5 w:0) // Storage: Nonfungible Owned (r:0 w:5) fn reject_nft() -> Weight { - (262_238_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(29 as Weight)) - .saturating_add(RocksDbWeight::get().writes(25 as Weight)) + (277_017_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(30 as Weight)) + .saturating_add(RocksDbWeight::get().writes(26 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) @@ -396,7 +400,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn set_property() -> Weight { - (55_187_000 as Weight) + (55_408_000 as Weight) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -406,7 +410,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn set_priority() -> Weight { - (54_092_000 as Weight) + (55_063_000 as Weight) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -417,7 +421,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn add_basic_resource() -> Weight { - (62_551_000 as Weight) + (64_656_000 as Weight) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -426,11 +430,11 @@ impl WeightInfo for () { // Storage: Common CollectionById (r:1 w:0) // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) - // Storage: Nonfungible TokenAuxProperties (r:1 w:1) + // Storage: Nonfungible TokenAuxProperties (r:2 w:2) fn add_composable_resource() -> Weight { - (61_778_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(10 as Weight)) - .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + (67_593_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(11 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } // Storage: RmrkCore UniqueCollectionId (r:1 w:0) // Storage: Common CollectionProperties (r:1 w:0) @@ -439,7 +443,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenProperties (r:1 w:1) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn add_slot_resource() -> Weight { - (62_700_000 as Weight) + (63_845_000 as Weight) .saturating_add(RocksDbWeight::get().reads(10 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -449,7 +453,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenAuxProperties (r:1 w:1) // Storage: Nonfungible TokenData (r:5 w:0) fn remove_resource() -> Weight { - (52_920_000 as Weight) + (53_414_000 as Weight) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -459,7 +463,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn accept_resource() -> Weight { - (52_450_000 as Weight) + (51_869_000 as Weight) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -469,7 +473,7 @@ impl WeightInfo for () { // Storage: Nonfungible TokenData (r:5 w:0) // Storage: Nonfungible TokenAuxProperties (r:1 w:1) fn accept_resource_removal() -> Weight { - (50_901_000 as Weight) + (53_168_000 as Weight) .saturating_add(RocksDbWeight::get().reads(9 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } diff --git a/pallets/proxy-rmrk-equip/src/weights.rs b/pallets/proxy-rmrk-equip/src/weights.rs index f2e4f4ef18..aca21e466f 100644 --- a/pallets/proxy-rmrk-equip/src/weights.rs +++ b/pallets/proxy-rmrk-equip/src/weights.rs @@ -3,7 +3,7 @@ //! Autogenerated weights for pallet_proxy_rmrk_equip //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-06-21, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-07-01, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -54,9 +54,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenData (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:1) fn create_base(b: u32, ) -> Weight { - (62_832_000 as Weight) - // Standard Error: 22_000 - .saturating_add((20_106_000 as Weight).saturating_mul(b as Weight)) + (57_871_000 as Weight) + // Standard Error: 21_000 + .saturating_add((19_870_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(8 as Weight)) @@ -71,9 +71,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Nonfungible TokenData (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:1) fn theme_add(b: u32, ) -> Weight { - (46_793_000 as Weight) - // Standard Error: 61_000 - .saturating_add((3_061_000 as Weight).saturating_mul(b as Weight)) + (46_121_000 as Weight) + // Standard Error: 31_000 + .saturating_add((2_988_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(5 as Weight)) } @@ -82,7 +82,7 @@ impl WeightInfo for SubstrateWeight { // Storage: RmrkEquip InernalPartId (r:1 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) fn equippable() -> Weight { - (32_495_000 as Weight) + (32_032_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -103,9 +103,9 @@ impl WeightInfo for () { // Storage: Nonfungible TokenData (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:1) fn create_base(b: u32, ) -> Weight { - (62_832_000 as Weight) - // Standard Error: 22_000 - .saturating_add((20_106_000 as Weight).saturating_mul(b as Weight)) + (57_871_000 as Weight) + // Standard Error: 21_000 + .saturating_add((19_870_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(8 as Weight)) @@ -120,9 +120,9 @@ impl WeightInfo for () { // Storage: Nonfungible TokenData (r:0 w:1) // Storage: Nonfungible Owned (r:0 w:1) fn theme_add(b: u32, ) -> Weight { - (46_793_000 as Weight) - // Standard Error: 61_000 - .saturating_add((3_061_000 as Weight).saturating_mul(b as Weight)) + (46_121_000 as Weight) + // Standard Error: 31_000 + .saturating_add((2_988_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } @@ -131,7 +131,7 @@ impl WeightInfo for () { // Storage: RmrkEquip InernalPartId (r:1 w:0) // Storage: Nonfungible TokenProperties (r:1 w:1) fn equippable() -> Weight { - (32_495_000 as Weight) + (32_032_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } diff --git a/pallets/refungible/src/benchmarking.rs b/pallets/refungible/src/benchmarking.rs index 57f19318c1..cb92910417 100644 --- a/pallets/refungible/src/benchmarking.rs +++ b/pallets/refungible/src/benchmarking.rs @@ -203,4 +203,12 @@ benchmarks! { let item = create_max_item(&collection, &owner, [(sender.clone(), 200)])?; >::set_allowance(&collection, &sender, &burner, item, 200)?; }: {>::burn_from(&collection, &burner, &sender, item, 200, &Unlimited)?} + + repartition_item { + bench_init!{ + owner: sub; collection: collection(owner); + sender: cross_from_sub(owner); owner: cross_sub; + }; + let item = create_max_item(&collection, &sender, [(owner.clone(), 100)])?; + }: {>::repartition(&collection, &owner, item, 200)?} } diff --git a/pallets/refungible/src/common.rs b/pallets/refungible/src/common.rs index 488807e8c3..eb0c1930f8 100644 --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -22,9 +22,9 @@ use up_data_structs::{ CollectionId, TokenId, CreateItemExData, CreateRefungibleExData, budget::Budget, Property, PropertyKey, PropertyValue, PropertyKeyPermission, CreateItemData, }; -use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight}; +use pallet_common::{CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight}; use pallet_structure::Error as StructureError; -use sp_runtime::DispatchError; +use sp_runtime::{DispatchError}; use sp_std::{vec::Vec, vec}; use crate::{ @@ -314,6 +314,7 @@ impl CommonCollectionOperations for RefungibleHandle { _sender: T::CrossAccountId, _token_id: TokenId, _property: Vec, + _nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { fail!(>::SettingPropertiesNotAllowed) } @@ -331,6 +332,7 @@ impl CommonCollectionOperations for RefungibleHandle { _sender: T::CrossAccountId, _token_id: TokenId, _property_keys: Vec, + _nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { fail!(>::SettingPropertiesNotAllowed) } @@ -340,7 +342,7 @@ impl CommonCollectionOperations for RefungibleHandle { _sender: ::CrossAccountId, _from: (CollectionId, TokenId), _under: TokenId, - _budget: &dyn Budget, + _nesting_budget: &dyn Budget, ) -> sp_runtime::DispatchResult { fail!(>::RefungibleDisallowsNesting) } @@ -405,4 +407,22 @@ impl CommonCollectionOperations for RefungibleHandle { ) -> u128 { >::get((self.id, token, sender, spender)) } + + fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions> { + Some(self) + } +} + +impl RefungibleExtensions for RefungibleHandle { + fn repartition( + &self, + owner: &T::CrossAccountId, + token: TokenId, + amount: u128, + ) -> DispatchResultWithPostInfo { + with_weight( + >::repartition(self, owner, token, amount), + >::repartition_item(), + ) + } } diff --git a/pallets/refungible/src/lib.rs b/pallets/refungible/src/lib.rs index 3d833e5074..78fb1417ad 100644 --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -64,6 +64,8 @@ pub mod pallet { NotRefungibleDataUsedToMintFungibleCollectionToken, /// Maximum refungibility exceeded WrongRefungiblePieces, + /// Refungible token can't be repartitioned by user who isn't owns all pieces + RepartitionWhileNotOwningAllPieces, /// Refungible token can't nest other tokens RefungibleDisallowsNesting, /// Setting item properties is not allowed @@ -684,4 +686,28 @@ impl Pallet { ) -> DispatchResult { Self::create_multiple_items(collection, sender, vec![data], nesting_budget) } + + pub fn repartition( + collection: &RefungibleHandle, + owner: &T::CrossAccountId, + token: TokenId, + amount: u128, + ) -> DispatchResult { + ensure!( + amount <= MAX_REFUNGIBLE_PIECES, + >::WrongRefungiblePieces + ); + ensure!(amount > 0, >::TokenValueTooLow); + // Ensure user owns all pieces + let total_supply = >::get((collection.id, token)); + let balance = >::get((collection.id, token, owner)); + ensure!( + total_supply == balance, + >::RepartitionWhileNotOwningAllPieces + ); + + >::insert((collection.id, token, owner), amount); + >::insert((collection.id, token), amount); + Ok(()) + } } diff --git a/pallets/refungible/src/weights.rs b/pallets/refungible/src/weights.rs index 3afc1a173a..b04d9e04f8 100644 --- a/pallets/refungible/src/weights.rs +++ b/pallets/refungible/src/weights.rs @@ -3,7 +3,7 @@ //! Autogenerated weights for pallet_refungible //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-06-15, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-06-27, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -49,6 +49,7 @@ pub trait WeightInfo { fn transfer_from_removing() -> Weight; fn transfer_from_creating_removing() -> Weight; fn burn_from() -> Weight; + fn repartition_item() -> Weight; } /// Weights for pallet_refungible using the Substrate node and recommended hardware. @@ -61,7 +62,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn create_item() -> Weight { - (21_321_000 as Weight) + (17_553_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -72,9 +73,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible TokenData (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items(b: u32, ) -> Weight { - (16_313_000 as Weight) - // Standard Error: 4_000 - .saturating_add((5_464_000 as Weight).saturating_mul(b as Weight)) + (10_654_000 as Weight) + // Standard Error: 1_000 + .saturating_add((5_114_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) .saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(b as Weight))) @@ -86,9 +87,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible TokenData (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight { - (15_631_000 as Weight) - // Standard Error: 5_000 - .saturating_add((8_141_000 as Weight).saturating_mul(b as Weight)) + (3_587_000 as Weight) + // Standard Error: 2_000 + .saturating_add((7_931_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(1 as Weight)) @@ -101,9 +102,9 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible Balance (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight { - (11_191_000 as Weight) - // Standard Error: 4_000 - .saturating_add((6_321_000 as Weight).saturating_mul(b as Weight)) + (1_980_000 as Weight) + // Standard Error: 2_000 + .saturating_add((6_305_000 as Weight).saturating_mul(b as Weight)) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(T::DbWeight::get().writes(3 as Weight)) @@ -114,7 +115,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_item_partial() -> Weight { - (24_421_000 as Weight) + (21_010_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } @@ -125,13 +126,13 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_item_fully() -> Weight { - (32_900_000 as Weight) + (28_413_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } // Storage: Refungible Balance (r:2 w:2) fn transfer_normal() -> Weight { - (20_215_000 as Weight) + (17_513_000 as Weight) .saturating_add(T::DbWeight::get().reads(2 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -139,7 +140,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_creating() -> Weight { - (24_809_000 as Weight) + (20_469_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } @@ -147,7 +148,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_removing() -> Weight { - (26_704_000 as Weight) + (22_472_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } @@ -155,21 +156,21 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:2 w:2) // Storage: Refungible Owned (r:0 w:2) fn transfer_creating_removing() -> Weight { - (28_728_000 as Weight) + (24_866_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } // Storage: Refungible Balance (r:1 w:0) // Storage: Refungible Allowance (r:0 w:1) fn approve() -> Weight { - (16_107_000 as Weight) + (13_475_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Refungible Allowance (r:1 w:1) // Storage: Refungible Balance (r:2 w:2) fn transfer_from_normal() -> Weight { - (28_765_000 as Weight) + (24_707_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -178,7 +179,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_from_creating() -> Weight { - (32_788_000 as Weight) + (27_812_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(5 as Weight)) } @@ -187,7 +188,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_from_removing() -> Weight { - (34_523_000 as Weight) + (29_966_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(5 as Weight)) } @@ -196,7 +197,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible AccountBalance (r:2 w:2) // Storage: Refungible Owned (r:0 w:2) fn transfer_from_creating_removing() -> Weight { - (36_749_000 as Weight) + (31_660_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(7 as Weight)) } @@ -208,10 +209,17 @@ impl WeightInfo for SubstrateWeight { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_from() -> Weight { - (42_259_000 as Weight) + (36_248_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(7 as Weight)) } + // Storage: Refungible TotalSupply (r:1 w:1) + // Storage: Refungible Balance (r:1 w:1) + fn repartition_item() -> Weight { + (8_226_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } } // For backwards compatibility and tests @@ -223,7 +231,7 @@ impl WeightInfo for () { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn create_item() -> Weight { - (21_321_000 as Weight) + (17_553_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } @@ -234,9 +242,9 @@ impl WeightInfo for () { // Storage: Refungible TokenData (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items(b: u32, ) -> Weight { - (16_313_000 as Weight) - // Standard Error: 4_000 - .saturating_add((5_464_000 as Weight).saturating_mul(b as Weight)) + (10_654_000 as Weight) + // Standard Error: 1_000 + .saturating_add((5_114_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) .saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(b as Weight))) @@ -248,9 +256,9 @@ impl WeightInfo for () { // Storage: Refungible TokenData (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight { - (15_631_000 as Weight) - // Standard Error: 5_000 - .saturating_add((8_141_000 as Weight).saturating_mul(b as Weight)) + (3_587_000 as Weight) + // Standard Error: 2_000 + .saturating_add((7_931_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) @@ -263,9 +271,9 @@ impl WeightInfo for () { // Storage: Refungible Balance (r:0 w:4) // Storage: Refungible Owned (r:0 w:4) fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight { - (11_191_000 as Weight) - // Standard Error: 4_000 - .saturating_add((6_321_000 as Weight).saturating_mul(b as Weight)) + (1_980_000 as Weight) + // Standard Error: 2_000 + .saturating_add((6_305_000 as Weight).saturating_mul(b as Weight)) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight))) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) @@ -276,7 +284,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_item_partial() -> Weight { - (24_421_000 as Weight) + (21_010_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } @@ -287,13 +295,13 @@ impl WeightInfo for () { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_item_fully() -> Weight { - (32_900_000 as Weight) + (28_413_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } // Storage: Refungible Balance (r:2 w:2) fn transfer_normal() -> Weight { - (20_215_000 as Weight) + (17_513_000 as Weight) .saturating_add(RocksDbWeight::get().reads(2 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -301,7 +309,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_creating() -> Weight { - (24_809_000 as Weight) + (20_469_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } @@ -309,7 +317,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_removing() -> Weight { - (26_704_000 as Weight) + (22_472_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } @@ -317,21 +325,21 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:2 w:2) // Storage: Refungible Owned (r:0 w:2) fn transfer_creating_removing() -> Weight { - (28_728_000 as Weight) + (24_866_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } // Storage: Refungible Balance (r:1 w:0) // Storage: Refungible Allowance (r:0 w:1) fn approve() -> Weight { - (16_107_000 as Weight) + (13_475_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Refungible Allowance (r:1 w:1) // Storage: Refungible Balance (r:2 w:2) fn transfer_from_normal() -> Weight { - (28_765_000 as Weight) + (24_707_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } @@ -340,7 +348,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_from_creating() -> Weight { - (32_788_000 as Weight) + (27_812_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } @@ -349,7 +357,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:1 w:1) // Storage: Refungible Owned (r:0 w:1) fn transfer_from_removing() -> Weight { - (34_523_000 as Weight) + (29_966_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(5 as Weight)) } @@ -358,7 +366,7 @@ impl WeightInfo for () { // Storage: Refungible AccountBalance (r:2 w:2) // Storage: Refungible Owned (r:0 w:2) fn transfer_from_creating_removing() -> Weight { - (36_749_000 as Weight) + (31_660_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } @@ -370,8 +378,15 @@ impl WeightInfo for () { // Storage: Refungible TokenData (r:0 w:1) // Storage: Refungible Owned (r:0 w:1) fn burn_from() -> Weight { - (42_259_000 as Weight) + (36_248_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(7 as Weight)) } + // Storage: Refungible TotalSupply (r:1 w:1) + // Storage: Refungible Balance (r:1 w:1) + fn repartition_item() -> Weight { + (8_226_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().writes(2 as Weight)) + } } diff --git a/pallets/unique/src/eth/mod.rs b/pallets/unique/src/eth/mod.rs index 5475081ffa..c6276f154f 100644 --- a/pallets/unique/src/eth/mod.rs +++ b/pallets/unique/src/eth/mod.rs @@ -48,7 +48,7 @@ impl WithRecorder for EvmCollectionHelpers { impl EvmCollectionHelpers { #[weight(>::create_collection())] fn create_nonfungible_collection( - &self, + &mut self, caller: caller, name: string, description: string, diff --git a/pallets/unique/src/eth/stubs/CollectionHelpers.raw b/pallets/unique/src/eth/stubs/CollectionHelpers.raw index a131288f7d..549328051a 100644 Binary files a/pallets/unique/src/eth/stubs/CollectionHelpers.raw and b/pallets/unique/src/eth/stubs/CollectionHelpers.raw differ diff --git a/pallets/unique/src/eth/stubs/CollectionHelpers.sol b/pallets/unique/src/eth/stubs/CollectionHelpers.sol index 49fc553e1b..e46a36e37e 100644 --- a/pallets/unique/src/eth/stubs/CollectionHelpers.sol +++ b/pallets/unique/src/eth/stubs/CollectionHelpers.sol @@ -36,12 +36,12 @@ contract CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { string memory name, string memory description, string memory tokenPrefix - ) public view returns (address) { + ) public returns (address) { require(false, stub_error); name; description; tokenPrefix; - dummy; + dummy = 0; return 0x0000000000000000000000000000000000000000; } diff --git a/pallets/unique/src/lib.rs b/pallets/unique/src/lib.rs index 2aeb03bd97..f935c89688 100644 --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -27,7 +27,7 @@ extern crate alloc; use frame_support::{ decl_module, decl_storage, decl_error, decl_event, dispatch::DispatchResult, - ensure, + ensure, fail, weights::{Weight}, transactional, pallet_prelude::{DispatchResultWithPostInfo, ConstU32}, @@ -45,7 +45,7 @@ use up_data_structs::{ use pallet_evm::account::CrossAccountId; use pallet_common::{ CollectionHandle, Pallet as PalletCommon, CommonWeightInfo, dispatch::dispatch_tx, - dispatch::CollectionDispatch, + dispatch::CollectionDispatch, RefungibleExtensionsWeightInfo, }; pub mod eth; @@ -65,6 +65,8 @@ decl_error! { ConfirmUnsetSponsorFail, /// Length of items properties must be greater than 0. EmptyArgument, + /// Repertition is only supported by refungible collection + RepartitionCalledOnNonRefungibleCollection, } } @@ -74,6 +76,7 @@ pub trait Config: system::Config + pallet_common::Config + Sized + TypeInfo { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; type CommonWeightInfo: CommonWeightInfo; + type RefungibleExtensionsWeightInfo: RefungibleExtensionsWeightInfo; } decl_event! { @@ -651,8 +654,9 @@ decl_module! { ensure!(!properties.is_empty(), Error::::EmptyArgument); let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(NESTING_BUDGET); - dispatch_tx::(collection_id, |d| d.set_token_properties(sender, token_id, properties)) + dispatch_tx::(collection_id, |d| d.set_token_properties(sender, token_id, properties, &budget)) } #[weight = T::CommonWeightInfo::delete_token_properties(property_keys.len() as u32)] @@ -666,8 +670,9 @@ decl_module! { ensure!(!property_keys.is_empty(), Error::::EmptyArgument); let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(NESTING_BUDGET); - dispatch_tx::(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys)) + dispatch_tx::(collection_id, |d| d.delete_token_properties(sender, token_id, property_keys, &budget)) } #[weight = T::CommonWeightInfo::set_token_property_permissions(property_permissions.len() as u32)] @@ -898,5 +903,23 @@ decl_module! { target_collection.save() } + + #[weight = T::RefungibleExtensionsWeightInfo::repartition()] + #[transactional] + pub fn repartition( + origin, + collection_id: CollectionId, + token: TokenId, + amount: u128, + ) -> DispatchResultWithPostInfo { + let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + dispatch_tx::(collection_id, |d| { + if let Some(refungible_extensions) = d.refungible_extensions() { + refungible_extensions.repartition(&sender, token, amount) + } else { + fail!(>::RepartitionCalledOnNonRefungibleCollection) + } + }) + } } } diff --git a/pallets/unique/src/weights.rs b/pallets/unique/src/weights.rs index 4a356f9456..f13ae907fd 100644 --- a/pallets/unique/src/weights.rs +++ b/pallets/unique/src/weights.rs @@ -3,7 +3,7 @@ //! Autogenerated weights for pallet_unique //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-06-15, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-06-28, STEPS: `50`, REPEAT: 80, LOW RANGE: `[]`, HIGH RANGE: `[]` //! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 1024 // Executed Command: @@ -57,7 +57,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Common CollectionProperties (r:0 w:1) // Storage: Common CollectionById (r:0 w:1) fn create_collection() -> Weight { - (39_427_000 as Weight) + (53_511_000 as Weight) .saturating_add(T::DbWeight::get().reads(4 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -69,27 +69,27 @@ impl WeightInfo for SubstrateWeight { // Storage: Common AdminAmount (r:0 w:1) // Storage: Common CollectionProperties (r:0 w:1) fn destroy_collection() -> Weight { - (48_339_000 as Weight) + (69_481_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } // Storage: Common CollectionById (r:1 w:0) // Storage: Common Allowlist (r:0 w:1) fn add_to_allow_list() -> Weight { - (17_379_000 as Weight) + (22_892_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:0) // Storage: Common Allowlist (r:0 w:1) fn remove_from_allow_list() -> Weight { - (17_490_000 as Weight) + (22_973_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn change_collection_owner() -> Weight { - (17_701_000 as Weight) + (22_392_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -97,7 +97,7 @@ impl WeightInfo for SubstrateWeight { // Storage: Common IsAdmin (r:1 w:1) // Storage: Common AdminAmount (r:1 w:1) fn add_collection_admin() -> Weight { - (23_301_000 as Weight) + (30_298_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } @@ -105,37 +105,37 @@ impl WeightInfo for SubstrateWeight { // Storage: Common IsAdmin (r:1 w:1) // Storage: Common AdminAmount (r:1 w:1) fn remove_collection_admin() -> Weight { - (24_859_000 as Weight) + (32_842_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(2 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_collection_sponsor() -> Weight { - (17_795_000 as Weight) + (22_613_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn confirm_sponsorship() -> Weight { - (17_297_000 as Weight) + (22_462_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn remove_collection_sponsor() -> Weight { - (17_079_000 as Weight) + (21_730_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_transfers_enabled_flag() -> Weight { - (9_734_000 as Weight) + (10_941_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_collection_limits() -> Weight { - (17_998_000 as Weight) + (22_363_000 as Weight) .saturating_add(T::DbWeight::get().reads(1 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } @@ -150,7 +150,7 @@ impl WeightInfo for () { // Storage: Common CollectionProperties (r:0 w:1) // Storage: Common CollectionById (r:0 w:1) fn create_collection() -> Weight { - (39_427_000 as Weight) + (53_511_000 as Weight) .saturating_add(RocksDbWeight::get().reads(4 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } @@ -162,27 +162,27 @@ impl WeightInfo for () { // Storage: Common AdminAmount (r:0 w:1) // Storage: Common CollectionProperties (r:0 w:1) fn destroy_collection() -> Weight { - (48_339_000 as Weight) + (69_481_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } // Storage: Common CollectionById (r:1 w:0) // Storage: Common Allowlist (r:0 w:1) fn add_to_allow_list() -> Weight { - (17_379_000 as Weight) + (22_892_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:0) // Storage: Common Allowlist (r:0 w:1) fn remove_from_allow_list() -> Weight { - (17_490_000 as Weight) + (22_973_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn change_collection_owner() -> Weight { - (17_701_000 as Weight) + (22_392_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } @@ -190,7 +190,7 @@ impl WeightInfo for () { // Storage: Common IsAdmin (r:1 w:1) // Storage: Common AdminAmount (r:1 w:1) fn add_collection_admin() -> Weight { - (23_301_000 as Weight) + (30_298_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } @@ -198,37 +198,37 @@ impl WeightInfo for () { // Storage: Common IsAdmin (r:1 w:1) // Storage: Common AdminAmount (r:1 w:1) fn remove_collection_admin() -> Weight { - (24_859_000 as Weight) + (32_842_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(2 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_collection_sponsor() -> Weight { - (17_795_000 as Weight) + (22_613_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn confirm_sponsorship() -> Weight { - (17_297_000 as Weight) + (22_462_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn remove_collection_sponsor() -> Weight { - (17_079_000 as Weight) + (21_730_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_transfers_enabled_flag() -> Weight { - (9_734_000 as Weight) + (10_941_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } // Storage: Common CollectionById (r:1 w:1) fn set_collection_limits() -> Weight { - (17_998_000 as Weight) + (22_363_000 as Weight) .saturating_add(RocksDbWeight::get().reads(1 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } diff --git a/runtime/common/src/weights.rs b/runtime/common/src/weights.rs index 2b73a74897..f926c8d5ab 100644 --- a/runtime/common/src/weights.rs +++ b/runtime/common/src/weights.rs @@ -16,11 +16,13 @@ use core::marker::PhantomData; use frame_support::{weights::Weight}; -use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight}; +use pallet_common::{CommonWeightInfo, dispatch::dispatch_weight, RefungibleExtensionsWeightInfo}; use pallet_fungible::{Config as FungibleConfig, common::CommonWeights as FungibleWeights}; use pallet_nonfungible::{Config as NonfungibleConfig, common::CommonWeights as NonfungibleWeights}; -use pallet_refungible::{Config as RefungibleConfig, common::CommonWeights as RefungibleWeights}; +use pallet_refungible::{ + Config as RefungibleConfig, weights::WeightInfo, common::CommonWeights as RefungibleWeights, +}; use up_data_structs::{CreateItemExData, CreateItemData}; macro_rules! max_weight_of { @@ -98,3 +100,12 @@ where max_weight_of!(burn_recursively_breadth_raw(amount)) } } + +impl RefungibleExtensionsWeightInfo for CommonWeights +where + T: FungibleConfig + NonfungibleConfig + RefungibleConfig, +{ + fn repartition() -> Weight { + dispatch_weight::() + <::WeightInfo>::repartition_item() + } +} diff --git a/runtime/opal/src/lib.rs b/runtime/opal/src/lib.rs index 50c841e8cb..bded4eb54b 100644 --- a/runtime/opal/src/lib.rs +++ b/runtime/opal/src/lib.rs @@ -192,7 +192,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 924000, + spec_version: 924010, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -925,6 +925,7 @@ impl pallet_unique::Config for Runtime { type Event = Event; type WeightInfo = pallet_unique::weights::SubstrateWeight; type CommonWeightInfo = CommonWeights; + type RefungibleExtensionsWeightInfo = CommonWeights; } parameter_types! { diff --git a/runtime/quartz/src/lib.rs b/runtime/quartz/src/lib.rs index 9364dd1a4d..a322cc0654 100644 --- a/runtime/quartz/src/lib.rs +++ b/runtime/quartz/src/lib.rs @@ -191,7 +191,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 924000, + spec_version: 924010, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -924,6 +924,7 @@ impl pallet_unique::Config for Runtime { type Event = Event; type WeightInfo = pallet_unique::weights::SubstrateWeight; type CommonWeightInfo = CommonWeights; + type RefungibleExtensionsWeightInfo = CommonWeights; } parameter_types! { diff --git a/runtime/tests/src/lib.rs b/runtime/tests/src/lib.rs index 7d9c52d9f5..2a993e88c0 100644 --- a/runtime/tests/src/lib.rs +++ b/runtime/tests/src/lib.rs @@ -255,6 +255,7 @@ impl pallet_unique::Config for Test { type Event = (); type WeightInfo = (); type CommonWeightInfo = CommonWeights; + type RefungibleExtensionsWeightInfo = CommonWeights; } // Build genesis storage according to the mock runtime. diff --git a/runtime/tests/src/tests.rs b/runtime/tests/src/tests.rs index 1f7fe517bb..2ff49b9025 100644 --- a/runtime/tests/src/tests.rs +++ b/runtime/tests/src/tests.rs @@ -21,7 +21,7 @@ use up_data_structs::{ CreateReFungibleData, MAX_DECIMAL_POINTS, COLLECTION_ADMINS_LIMIT, TokenId, MAX_TOKEN_OWNERSHIP, CreateCollectionData, CollectionMode, AccessMode, CollectionPermissions, PropertyKeyPermission, PropertyPermission, Property, CollectionPropertiesVec, - CollectionPropertiesPermissionsVec, TokenChild, + CollectionPropertiesPermissionsVec, }; use frame_support::{assert_noop, assert_ok, assert_err}; use sp_std::convert::TryInto; diff --git a/runtime/unique/src/lib.rs b/runtime/unique/src/lib.rs index 5ea48f9609..63634c5717 100644 --- a/runtime/unique/src/lib.rs +++ b/runtime/unique/src/lib.rs @@ -190,7 +190,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!(RUNTIME_NAME), impl_name: create_runtime_str!(RUNTIME_NAME), authoring_version: 1, - spec_version: 924000, + spec_version: 924010, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -913,6 +913,7 @@ impl pallet_unique::Config for Runtime { type Event = Event; type WeightInfo = pallet_unique::weights::SubstrateWeight; type CommonWeightInfo = CommonWeights; + type RefungibleExtensionsWeightInfo = CommonWeights; } parameter_types! { diff --git a/tests/package.json b/tests/package.json index 0c3bcb8f02..804105848d 100644 --- a/tests/package.json +++ b/tests/package.json @@ -60,6 +60,7 @@ "testAddToContractAllowList": "mocha --timeout 9999999 -r ts-node/register ./**/addToContractAllowList.test.ts", "testTransfer": "mocha --timeout 9999999 -r ts-node/register ./**/transfer.test.ts", "testBurnItem": "mocha --timeout 9999999 -r ts-node/register ./**/burnItem.test.ts", + "testAdminTransferAndBurn": "mocha --timeout 9999999 -r ts-node/register ./**/adminTransferAndBurn.test.ts", "testSetMintPermission": "mocha --timeout 9999999 -r ts-node/register ./**/setMintPermission.test.ts", "testCreditFeesToTreasury": "mocha --timeout 9999999 -r ts-node/register ./**/creditFeesToTreasury.test.ts", "testContractSponsoring": "mocha --timeout 9999999 -r ts-node/register ./**/contractSponsoring.test.ts", @@ -78,6 +79,7 @@ "testEnableDisableTransfers": "mocha --timeout 9999999 -r ts-node/register ./**/enableDisableTransfer.test.ts", "testLimits": "mocha --timeout 9999999 -r ts-node/register ./**/limits.test.ts", "testEthCreateCollection": "mocha --timeout 9999999 -r ts-node/register ./**/eth/createCollection.test.ts", + "testRFT": "mocha --timeout 9999999 -r ts-node/register ./**/refungible.test.ts", "polkadot-types-fetch-metadata": "curl -H 'Content-Type: application/json' -d '{\"id\":\"1\", \"jsonrpc\":\"2.0\", \"method\": \"state_getMetadata\", \"params\":[]}' http://localhost:9933 > src/interfaces/metadata.json", "polkadot-types-from-defs": "ts-node ./node_modules/.bin/polkadot-types-from-defs --endpoint src/interfaces/metadata.json --input src/interfaces/ --package .", "polkadot-types-from-chain": "ts-node ./node_modules/.bin/polkadot-types-from-chain --endpoint src/interfaces/metadata.json --output src/interfaces/ --package .", diff --git a/tests/src/adminTransferAndBurn.test.ts b/tests/src/adminTransferAndBurn.test.ts new file mode 100644 index 0000000000..73fdfda8cc --- /dev/null +++ b/tests/src/adminTransferAndBurn.test.ts @@ -0,0 +1,72 @@ +// 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 . + +import {IKeyringPair} from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import {default as usingApi} from './substrate/substrate-api'; +import { + createCollectionExpectSuccess, + createItemExpectSuccess, + transferExpectFailure, + transferFromExpectSuccess, + burnItemExpectFailure, + burnFromExpectSuccess, + setCollectionLimitsExpectSuccess, +} from './util/helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('Integration Test: ownerCanTransfer allows admins to use only transferFrom/burnFrom:', () => { + let alice: IKeyringPair; + let bob: IKeyringPair; + let charlie: IKeyringPair; + + before(async () => { + await usingApi(async (api, privateKeyWrapper) => { + alice = privateKeyWrapper('//Alice'); + bob = privateKeyWrapper('//Bob'); + charlie = privateKeyWrapper('//Charlie'); + }); + }); + + it('admin transfers other user\'s token', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess(); + await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true}); + + const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', {Substrate: bob.address}); + + await transferExpectFailure(collectionId, tokenId, alice, charlie); + + await transferFromExpectSuccess(collectionId, tokenId, alice, bob, charlie, 1); + }); + }); + + it('admin burns other user\'s token', async () => { + await usingApi(async () => { + const collectionId = await createCollectionExpectSuccess(); + await setCollectionLimitsExpectSuccess(alice, collectionId, {ownerCanTransfer: true}); + + const tokenId = await createItemExpectSuccess(alice, collectionId, 'NFT', {Substrate: bob.address}); + + await burnItemExpectFailure(alice, collectionId, tokenId); + + await burnFromExpectSuccess(alice, bob, collectionId, tokenId); + }); + }); +}); diff --git a/tests/src/burnItem.test.ts b/tests/src/burnItem.test.ts index 44319d6875..7e686d186e 100644 --- a/tests/src/burnItem.test.ts +++ b/tests/src/burnItem.test.ts @@ -155,7 +155,7 @@ describe('integration test: ext. burnItem() with admin permissions:', () => { await addCollectionAdminExpectSuccess(alice, collectionId, bob.address); await usingApi(async (api) => { - const tx = api.tx.unique.burnItem(collectionId, tokenId, 1); + const tx = api.tx.unique.burnFrom(collectionId, {Substrate: alice.address}, tokenId, 1); const events = await submitTransactionAsync(bob, tx); const result = getGenericResult(events); diff --git a/tests/src/createMultipleItemsEx.test.ts b/tests/src/createMultipleItemsEx.test.ts index d7899a70f2..18250fa0ce 100644 --- a/tests/src/createMultipleItemsEx.test.ts +++ b/tests/src/createMultipleItemsEx.test.ts @@ -16,9 +16,9 @@ import {expect} from 'chai'; import usingApi, {executeTransaction} from './substrate/substrate-api'; -import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess} from './util/helpers'; +import {addCollectionAdminExpectSuccess, createCollectionExpectSuccess, createCollectionWithPropsExpectSuccess, getBalance, getLastTokenId} from './util/helpers'; -describe('createMultipleItemsEx', () => { +describe('Integration Test: createMultipleItemsEx', () => { it('can initialize multiple NFT with different owners', async () => { const collection = await createCollectionExpectSuccess({mode: {type: 'NFT'}}); await usingApi(async (api, privateKeyWrapper) => { @@ -128,6 +128,77 @@ describe('createMultipleItemsEx', () => { }); }); + it('can initialize fungible with multiple owners', async () => { + const collection = await createCollectionExpectSuccess({mode: {type: 'Fungible', decimalPoints: 0}}); + await usingApi(async (api, privateKeyWrapper) => { + const alice = privateKeyWrapper('//Alice'); + const bob = privateKeyWrapper('//Bob'); + + const users = new Map(); + users.set(JSON.stringify({Substrate: alice.address}), 50); + users.set(JSON.stringify({Substrate: bob.address}), 100); + + await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, { + Fungible: users, + })); + + expect(await getBalance(api, collection, alice.address, 0)).to.equal(50n); + expect(await getBalance(api, collection, bob.address, 0)).to.equal(100n); + }); + }); + + it('can initialize an RFT with multiple owners', async () => { + const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + await usingApi(async (api, privateKeyWrapper) => { + const alice = privateKeyWrapper('//Alice'); + const bob = privateKeyWrapper('//Bob'); + + const users = new Map(); + users.set(JSON.stringify({Substrate: alice.address}), 1); + users.set(JSON.stringify({Substrate: bob.address}), 2); + + await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, { + RefungibleMultipleOwners: { + users: users, + }, + })); + + const itemsListIndexAfter = await getLastTokenId(api, collection); + expect(itemsListIndexAfter).to.be.equal(1); + + expect(await getBalance(api, collection, alice.address, 1)).to.be.equal(1n); + expect(await getBalance(api, collection, bob.address, 1)).to.be.equal(2n); + }); + }); + + it('can initialize multiple RFTs with the same owner', async () => { + const collection = await createCollectionExpectSuccess({mode: {type: 'ReFungible'}}); + await usingApi(async (api, privateKeyWrapper) => { + const alice = privateKeyWrapper('//Alice'); + + const item1User = new Map(); + item1User.set(JSON.stringify({Substrate: alice.address}), 1); + + const item2User = new Map(); + item2User.set(JSON.stringify({Substrate: alice.address}), 3); + + await executeTransaction(api, alice, api.tx.unique.createMultipleItemsEx(collection, { + RefungibleMultipleItems: [ + {users: item1User}, + {users: item2User}, + ], + })); + + const itemsListIndexAfter = await getLastTokenId(api, collection); + expect(itemsListIndexAfter).to.be.equal(2); + + expect(await getBalance(api, collection, alice.address, 1)).to.be.equal(1n); + expect(await getBalance(api, collection, alice.address, 2)).to.be.equal(3n); + }); + }); +}); + +describe('Negative test: createMultipleItemsEx', () => { it('No editing rights', async () => { const collection = await createCollectionWithPropsExpectSuccess({properties: [{key: 'key1', value: 'v'}], propPerm: [{key: 'key1', permission: {mutable: true, collectionAdmin: false, tokenOwner: false}}]}); @@ -321,4 +392,3 @@ describe('createMultipleItemsEx', () => { }); }); }); -''; diff --git a/tests/src/eth/api/CollectionHelpers.sol b/tests/src/eth/api/CollectionHelpers.sol index f9adb9afc7..ddd545deb4 100644 --- a/tests/src/eth/api/CollectionHelpers.sol +++ b/tests/src/eth/api/CollectionHelpers.sol @@ -27,7 +27,7 @@ interface CollectionHelpers is Dummy, ERC165, CollectionHelpersEvents { string memory name, string memory description, string memory tokenPrefix - ) external view returns (address); + ) external returns (address); // Selector: isCollectionExist(address) c3de1494 function isCollectionExist(address collectionAddress) diff --git a/tests/src/eth/collectionHelpersAbi.json b/tests/src/eth/collectionHelpersAbi.json index 2f9296adf3..93d388eb22 100644 --- a/tests/src/eth/collectionHelpersAbi.json +++ b/tests/src/eth/collectionHelpersAbi.json @@ -26,7 +26,7 @@ ], "name": "createNonfungibleCollection", "outputs": [{ "internalType": "address", "name": "", "type": "address" }], - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { diff --git a/tests/src/interfaces/augment-api-consts.ts b/tests/src/interfaces/augment-api-consts.ts index 3fc2817f15..902377851a 100644 --- a/tests/src/interfaces/augment-api-consts.ts +++ b/tests/src/interfaces/augment-api-consts.ts @@ -2,10 +2,10 @@ /* eslint-disable */ import type { ApiTypes } from '@polkadot/api-base/types'; -import type { Option, Vec, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; +import type { Option, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { Codec } from '@polkadot/types-codec/types'; import type { Permill } from '@polkadot/types/interfaces/runtime'; -import type { FrameSupportPalletId, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion } from '@polkadot/types/lookup'; +import type { FrameSupportPalletId, FrameSupportWeightsRuntimeDbWeight, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, SpVersionRuntimeVersion } from '@polkadot/types/lookup'; declare module '@polkadot/api-base/types/consts' { export interface AugmentedConsts { @@ -110,10 +110,6 @@ declare module '@polkadot/api-base/types/consts' { [key: string]: Codec; }; transactionPayment: { - /** - * The polynomial that is applied in order to derive fee from length. - **/ - lengthToFee: Vec & AugmentedConst; /** * A fee mulitplier for `Operational` extrinsics to compute "virtual tip" to boost their * `priority` @@ -138,10 +134,6 @@ declare module '@polkadot/api-base/types/consts' { * transactions. **/ operationalFeeMultiplier: u8 & AugmentedConst; - /** - * The polynomial that is applied in order to derive fee from weight. - **/ - weightToFee: Vec & AugmentedConst; /** * Generic const **/ diff --git a/tests/src/interfaces/augment-api-errors.ts b/tests/src/interfaces/augment-api-errors.ts index ef9b8c224a..19a06cea37 100644 --- a/tests/src/interfaces/augment-api-errors.ts +++ b/tests/src/interfaces/augment-api-errors.ts @@ -428,6 +428,10 @@ declare module '@polkadot/api-base/types/errors' { * Refungible token can't nest other tokens **/ RefungibleDisallowsNesting: AugmentedError; + /** + * Refungible token can't be repartitioned by user who isn't owns all pieces + **/ + RepartitionWhileNotOwningAllPieces: AugmentedError; /** * Setting item properties is not allowed **/ @@ -444,6 +448,7 @@ declare module '@polkadot/api-base/types/errors' { rmrkCore: { CannotAcceptNonOwnedNft: AugmentedError; CannotRejectNonOwnedNft: AugmentedError; + CannotRejectNonPendingNft: AugmentedError; CannotSendToDescendentOrSelf: AugmentedError; CollectionFullOrLocked: AugmentedError; CollectionNotEmpty: AugmentedError; @@ -452,10 +457,12 @@ declare module '@polkadot/api-base/types/errors' { NftTypeEncodeError: AugmentedError; NoAvailableCollectionId: AugmentedError; NoAvailableNftId: AugmentedError; + NoAvailableResourceId: AugmentedError; NonTransferable: AugmentedError; NoPermission: AugmentedError; ResourceDoesntExist: AugmentedError; ResourceNotPending: AugmentedError; + RmrkPropertyIsNotFound: AugmentedError; RmrkPropertyKeyIsTooLong: AugmentedError; RmrkPropertyValueIsTooLong: AugmentedError; UnableToDecodeRmrkData: AugmentedError; @@ -469,6 +476,8 @@ declare module '@polkadot/api-base/types/errors' { NeedsDefaultThemeFirst: AugmentedError; NoAvailableBaseId: AugmentedError; NoAvailablePartId: AugmentedError; + NoEquippableOnFixedPart: AugmentedError; + PartDoesntExist: AugmentedError; PermissionError: AugmentedError; /** * Generic error @@ -598,6 +607,10 @@ declare module '@polkadot/api-base/types/errors' { * Length of items properties must be greater than 0. **/ EmptyArgument: AugmentedError; + /** + * Repertition is only supported by refungible collection + **/ + RepartitionCalledOnNonRefungibleCollection: AugmentedError; /** * Generic error **/ diff --git a/tests/src/interfaces/augment-api-events.ts b/tests/src/interfaces/augment-api-events.ts index a35c9316a4..6698d664fd 100644 --- a/tests/src/interfaces/augment-api-events.ts +++ b/tests/src/interfaces/augment-api-events.ts @@ -13,45 +13,45 @@ declare module '@polkadot/api-base/types/events' { /** * A balance was set by root. **/ - BalanceSet: AugmentedEvent; + BalanceSet: AugmentedEvent; /** * Some amount was deposited (e.g. for transaction fees). **/ - Deposit: AugmentedEvent; + Deposit: AugmentedEvent; /** * An account was removed whose balance was non-zero but below ExistentialDeposit, * resulting in an outright loss. **/ - DustLost: AugmentedEvent; + DustLost: AugmentedEvent; /** * An account was created with some free balance. **/ - Endowed: AugmentedEvent; + Endowed: AugmentedEvent; /** * Some balance was reserved (moved from free to reserved). **/ - Reserved: AugmentedEvent; + Reserved: AugmentedEvent; /** * Some balance was moved from the reserve of the first account to the second account. * Final argument indicates the destination balance type. **/ - ReserveRepatriated: AugmentedEvent; + ReserveRepatriated: AugmentedEvent; /** * Some amount was removed from the account (e.g. for misbehavior). **/ - Slashed: AugmentedEvent; + Slashed: AugmentedEvent; /** * Transfer succeeded. **/ - Transfer: AugmentedEvent; + Transfer: AugmentedEvent; /** * Some balance was unreserved (moved from reserved to free). **/ - Unreserved: AugmentedEvent; + Unreserved: AugmentedEvent; /** * Some amount was withdrawn from the account (e.g. for transaction fees). **/ - Withdraw: AugmentedEvent; + Withdraw: AugmentedEvent; /** * Generic event **/ @@ -166,32 +166,26 @@ declare module '@polkadot/api-base/types/events' { dmpQueue: { /** * Downward message executed with the given outcome. - * \[ id, outcome \] **/ ExecutedDownward: AugmentedEvent; /** * Downward message is invalid XCM. - * \[ id \] **/ InvalidFormat: AugmentedEvent; /** * Downward message is overweight and was placed in the overweight queue. - * \[ id, index, required \] **/ OverweightEnqueued: AugmentedEvent; /** * Downward message from the overweight queue was executed. - * \[ index, used \] **/ OverweightServiced: AugmentedEvent; /** * Downward message is unsupported version of XCM. - * \[ id \] **/ UnsupportedVersion: AugmentedEvent; /** * The weight limit for handling downward messages was reached. - * \[ id, remaining, required \] **/ WeightExhausted: AugmentedEvent; /** @@ -246,12 +240,10 @@ declare module '@polkadot/api-base/types/events' { parachainSystem: { /** * Downward messages were processed using the given weight. - * \[ weight_used, result_mqc_head \] **/ DownwardMessagesProcessed: AugmentedEvent; /** * Some downward messages have been received and will be processed. - * \[ count \] **/ DownwardMessagesReceived: AugmentedEvent; /** @@ -398,28 +390,29 @@ declare module '@polkadot/api-base/types/events' { [key: string]: AugmentedEvent; }; rmrkCore: { - CollectionCreated: AugmentedEvent; - CollectionDestroyed: AugmentedEvent; - CollectionLocked: AugmentedEvent; - IssuerChanged: AugmentedEvent; - NFTAccepted: AugmentedEvent; - NFTBurned: AugmentedEvent; - NftMinted: AugmentedEvent; - NFTRejected: AugmentedEvent; - NFTSent: AugmentedEvent; - PrioritySet: AugmentedEvent; - PropertySet: AugmentedEvent, key: Bytes, value: Bytes], { collectionId: u32, maybeNftId: Option, key: Bytes, value: Bytes }>; - ResourceAccepted: AugmentedEvent; - ResourceAdded: AugmentedEvent; - ResourceRemoval: AugmentedEvent; - ResourceRemovalAccepted: AugmentedEvent; + CollectionCreated: AugmentedEvent; + CollectionDestroyed: AugmentedEvent; + CollectionLocked: AugmentedEvent; + IssuerChanged: AugmentedEvent; + NFTAccepted: AugmentedEvent; + NFTBurned: AugmentedEvent; + NftMinted: AugmentedEvent; + NFTRejected: AugmentedEvent; + NFTSent: AugmentedEvent; + PrioritySet: AugmentedEvent; + PropertySet: AugmentedEvent, Bytes, Bytes]>; + ResourceAccepted: AugmentedEvent; + ResourceAdded: AugmentedEvent; + ResourceRemoval: AugmentedEvent; + ResourceRemovalAccepted: AugmentedEvent; /** * Generic event **/ [key: string]: AugmentedEvent; }; rmrkEquip: { - BaseCreated: AugmentedEvent; + BaseCreated: AugmentedEvent; + EquippablesUpdated: AugmentedEvent; /** * Generic event **/ @@ -429,19 +422,19 @@ declare module '@polkadot/api-base/types/events' { /** * The call for the provided hash was not found so the task has been aborted. **/ - CallLookupFailed: AugmentedEvent, id: Option, error: FrameSupportScheduleLookupError], { task: ITuple<[u32, u32]>, id: Option, error: FrameSupportScheduleLookupError }>; + CallLookupFailed: AugmentedEvent, Option, FrameSupportScheduleLookupError]>; /** * Canceled some task. **/ - Canceled: AugmentedEvent; + Canceled: AugmentedEvent; /** * Dispatched some task. **/ - Dispatched: AugmentedEvent, id: Option, result: Result], { task: ITuple<[u32, u32]>, id: Option, result: Result }>; + Dispatched: AugmentedEvent, Option, Result]>; /** * Scheduled some task. **/ - Scheduled: AugmentedEvent; + Scheduled: AugmentedEvent; /** * Generic event **/ @@ -461,15 +454,15 @@ declare module '@polkadot/api-base/types/events' { /** * The \[sudoer\] just switched identity; the old key is supplied if one existed. **/ - KeyChanged: AugmentedEvent], { oldSudoer: Option }>; + KeyChanged: AugmentedEvent]>; /** * A sudo just took place. \[result\] **/ - Sudid: AugmentedEvent], { sudoResult: Result }>; + Sudid: AugmentedEvent]>; /** * A sudo just took place. \[result\] **/ - SudoAsDone: AugmentedEvent], { sudoResult: Result }>; + SudoAsDone: AugmentedEvent]>; /** * Generic event **/ @@ -483,23 +476,23 @@ declare module '@polkadot/api-base/types/events' { /** * An extrinsic failed. **/ - ExtrinsicFailed: AugmentedEvent; + ExtrinsicFailed: AugmentedEvent; /** * An extrinsic completed successfully. **/ - ExtrinsicSuccess: AugmentedEvent; + ExtrinsicSuccess: AugmentedEvent; /** * An account was reaped. **/ - KilledAccount: AugmentedEvent; + KilledAccount: AugmentedEvent; /** * A new account was created. **/ - NewAccount: AugmentedEvent; + NewAccount: AugmentedEvent; /** * On on-chain remark happened. **/ - Remarked: AugmentedEvent; + Remarked: AugmentedEvent; /** * Generic event **/ @@ -509,31 +502,31 @@ declare module '@polkadot/api-base/types/events' { /** * Some funds have been allocated. **/ - Awarded: AugmentedEvent; + Awarded: AugmentedEvent; /** * Some of our funds have been burnt. **/ - Burnt: AugmentedEvent; + Burnt: AugmentedEvent; /** * Some funds have been deposited. **/ - Deposit: AugmentedEvent; + Deposit: AugmentedEvent; /** * New proposal. **/ - Proposed: AugmentedEvent; + Proposed: AugmentedEvent; /** * A proposal was rejected; funds were slashed. **/ - Rejected: AugmentedEvent; + Rejected: AugmentedEvent; /** * Spending has finished; this is the amount that rolls over until next spend. **/ - Rollover: AugmentedEvent; + Rollover: AugmentedEvent; /** * We have ended a spend period and will now allocate funds. **/ - Spending: AugmentedEvent; + Spending: AugmentedEvent; /** * Generic event **/ @@ -636,15 +629,15 @@ declare module '@polkadot/api-base/types/events' { /** * Claimed vesting. **/ - Claimed: AugmentedEvent; + Claimed: AugmentedEvent; /** * Added new vesting schedule. **/ - VestingScheduleAdded: AugmentedEvent; + VestingScheduleAdded: AugmentedEvent; /** * Updated vesting schedules. **/ - VestingSchedulesUpdated: AugmentedEvent; + VestingSchedulesUpdated: AugmentedEvent; /** * Generic event **/ diff --git a/tests/src/interfaces/augment-api-query.ts b/tests/src/interfaces/augment-api-query.ts index 9f0ef29ea0..6ed2a40ec5 100644 --- a/tests/src/interfaces/augment-api-query.ts +++ b/tests/src/interfaces/augment-api-query.ts @@ -5,7 +5,7 @@ import type { ApiTypes } from '@polkadot/api-base/types'; import type { BTreeMap, Bytes, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64 } from '@polkadot/types-codec'; import type { AnyNumber, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, H160, H256 } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsTokenChild } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueQueueConfigData, EthereumBlock, EthereumLog, EthereumReceiptReceiptV3, EthereumTransactionTransactionV2, FpRpcTransactionStatus, FrameSupportWeightsPerDispatchClassU64, FrameSystemAccountInfo, FrameSystemEventRecord, FrameSystemLastRuntimeUpgradeInfo, FrameSystemPhase, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesReleases, PalletBalancesReserveData, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmContractHelpersSponsoringModeT, PalletNonfungibleItemData, PalletRefungibleItemData, PalletTransactionPaymentReleases, PalletTreasuryProposal, PalletUniqueSchedulerScheduledV3, PhantomTypeUpDataStructs, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, SpRuntimeDigest, SpTrieStorageProof, UpDataStructsCollection, UpDataStructsCollectionStats, UpDataStructsProperties, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsTokenChild } from '@polkadot/types/lookup'; import type { Observable } from '@polkadot/types/types'; declare module '@polkadot/api-base/types/storage' { @@ -228,6 +228,7 @@ declare module '@polkadot/api-base/types/storage' { * Used to enumerate tokens owned by account **/ owned: AugmentedQuery Observable, [u32, PalletEvmAccountBasicCrossAccountIdRepr, u32]> & QueryableStorageEntry; + tokenAuxProperties: AugmentedQuery Observable>, [u32, u32, UpDataStructsPropertyScope, Bytes]> & QueryableStorageEntry; /** * Used to enumerate token's children **/ diff --git a/tests/src/interfaces/augment-api-tx.ts b/tests/src/interfaces/augment-api-tx.ts index eb1ef110d9..42a78fd5a1 100644 --- a/tests/src/interfaces/augment-api-tx.ts +++ b/tests/src/interfaces/augment-api-tx.ts @@ -5,7 +5,7 @@ import type { ApiTypes } from '@polkadot/api-base/types'; import type { Bytes, Compact, Option, U256, U8aFixed, Vec, bool, u128, u16, u32, u64, u8 } from '@polkadot/types-codec'; import type { AnyNumber, IMethod, ITuple } from '@polkadot/types-codec/types'; import type { AccountId32, Call, H160, H256, MultiAddress, Perbill, Permill } from '@polkadot/types/interfaces/runtime'; -import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, FrameSupportScheduleMaybeHashed, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPrimitivesParachainInherentParachainInherentData, EthereumTransactionTransactionV2, FrameSupportScheduleMaybeHashed, OrmlVestingVestingSchedule, PalletEvmAccountBasicCrossAccountIdRepr, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsPartEquippableList, RmrkTraitsPartPartType, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCreateCollectionData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, XcmV1MultiLocation, XcmV2WeightLimit, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/api-base/types/submittable' { export interface AugmentedSubmittables { @@ -361,11 +361,11 @@ declare module '@polkadot/api-base/types/submittable' { /** * accept the addition of a new resource to an existing NFT **/ - acceptResource: AugmentedSubmittable<(rmrkCollectionId: u32 | AnyNumber | Uint8Array, rmrkNftId: u32 | AnyNumber | Uint8Array, rmrkResourceId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u32]>; + acceptResource: AugmentedSubmittable<(rmrkCollectionId: u32 | AnyNumber | Uint8Array, rmrkNftId: u32 | AnyNumber | Uint8Array, resourceId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u32]>; /** * accept the removal of a resource of an existing NFT **/ - acceptResourceRemoval: AugmentedSubmittable<(rmrkCollectionId: u32 | AnyNumber | Uint8Array, rmrkNftId: u32 | AnyNumber | Uint8Array, rmrkResourceId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u32]>; + acceptResourceRemoval: AugmentedSubmittable<(rmrkCollectionId: u32 | AnyNumber | Uint8Array, rmrkNftId: u32 | AnyNumber | Uint8Array, resourceId: u32 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u32]>; /** * Create basic resource **/ @@ -415,7 +415,7 @@ declare module '@polkadot/api-base/types/submittable' { * - `metadata`: Arbitrary data about an nft, e.g. IPFS hash * - `transferable`: Ability to transfer this NFT **/ - mintNft: AugmentedSubmittable<(owner: AccountId32 | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, recipient: Option | null | object | string | Uint8Array, royaltyAmount: Option | null | object | string | Uint8Array, metadata: Bytes | string | Uint8Array, transferable: bool | boolean | Uint8Array, resources: Option> | null | object | string | Uint8Array) => SubmittableExtrinsic, [AccountId32, u32, Option, Option, Bytes, bool, Option>]>; + mintNft: AugmentedSubmittable<(owner: Option | null | object | string | Uint8Array, collectionId: u32 | AnyNumber | Uint8Array, recipient: Option | null | object | string | Uint8Array, royaltyAmount: Option | null | object | string | Uint8Array, metadata: Bytes | string | Uint8Array, transferable: bool | boolean | Uint8Array, resources: Option> | null | object | string | Uint8Array) => SubmittableExtrinsic, [Option, u32, Option, Option, Bytes, bool, Option>]>; /** * Rejects an NFT sent from another account to self or owned NFT * @@ -465,6 +465,7 @@ declare module '@polkadot/api-base/types/submittable' { * RmrkPartsLimit **/ createBase: AugmentedSubmittable<(baseType: Bytes | string | Uint8Array, symbol: Bytes | string | Uint8Array, parts: Vec | (RmrkTraitsPartPartType | { FixedPart: any } | { SlotPart: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [Bytes, Bytes, Vec]>; + equippable: AugmentedSubmittable<(baseId: u32 | AnyNumber | Uint8Array, slotId: u32 | AnyNumber | Uint8Array, equippables: RmrkTraitsPartEquippableList | { All: any } | { Empty: any } | { Custom: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, u32, RmrkTraitsPartEquippableList]>; /** * 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) @@ -906,7 +907,7 @@ declare module '@polkadot/api-base/types/submittable' { deleteCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; deleteTokenProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, tokenId: u32 | AnyNumber | Uint8Array, propertyKeys: Vec | (Bytes | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, u32, Vec]>; /** - * **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money. + * Destroys collection if no tokens within this collection * * # Permissions * @@ -959,6 +960,7 @@ declare module '@polkadot/api-base/types/submittable' { * * address. **/ removeFromAllowList: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, address: PalletEvmAccountBasicCrossAccountIdRepr | { Substrate: any } | { Ethereum: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, PalletEvmAccountBasicCrossAccountIdRepr]>; + repartition: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, token: u32 | AnyNumber | Uint8Array, amount: u128 | AnyNumber | Uint8Array) => SubmittableExtrinsic, [u32, u32, u128]>; setCollectionLimits: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionLimits | { accountTokenOwnershipLimit?: any; sponsoredDataSize?: any; sponsoredDataRateLimit?: any; tokenLimit?: any; sponsorTransferTimeout?: any; sponsorApproveTimeout?: any; ownerCanTransfer?: any; ownerCanDestroy?: any; transfersEnabled?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionLimits]>; setCollectionPermissions: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, newLimit: UpDataStructsCollectionPermissions | { access?: any; mintMode?: any; nesting?: any } | string | Uint8Array) => SubmittableExtrinsic, [u32, UpDataStructsCollectionPermissions]>; setCollectionProperties: AugmentedSubmittable<(collectionId: u32 | AnyNumber | Uint8Array, properties: Vec | (UpDataStructsProperty | { key?: any; value?: any } | string | Uint8Array)[]) => SubmittableExtrinsic, [u32, Vec]>; diff --git a/tests/src/interfaces/augment-types.ts b/tests/src/interfaces/augment-types.ts index 809e65272e..e0d8942652 100644 --- a/tests/src/interfaces/augment-types.ts +++ b/tests/src/interfaces/augment-types.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-defs`, do not edit /* eslint-disable */ -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from './default'; import type { Data, StorageKey } from '@polkadot/types'; import type { BitVec, Bool, Bytes, I128, I16, I256, I32, I64, I8, Json, Null, OptionBool, Raw, Text, Type, U128, U16, U256, U32, U64, U8, USize, bool, i128, i16, i256, i32, i64, i8, u128, u16, u256, u32, u64, u8, usize } from '@polkadot/types-codec'; import type { AssetApproval, AssetApprovalKey, AssetBalance, AssetDestroyWitness, AssetDetails, AssetMetadata, TAssetBalance, TAssetDepositBalance } from '@polkadot/types/interfaces/assets'; @@ -489,7 +489,6 @@ declare module '@polkadot/types/types/registry' { FrameSupportWeightsPerDispatchClassU64: FrameSupportWeightsPerDispatchClassU64; FrameSupportWeightsPerDispatchClassWeightsPerClass: FrameSupportWeightsPerDispatchClassWeightsPerClass; FrameSupportWeightsRuntimeDbWeight: FrameSupportWeightsRuntimeDbWeight; - FrameSupportWeightsWeightToFeeCoefficient: FrameSupportWeightsWeightToFeeCoefficient; FrameSystemAccountInfo: FrameSystemAccountInfo; FrameSystemCall: FrameSystemCall; FrameSystemError: FrameSystemError; @@ -1226,6 +1225,7 @@ declare module '@polkadot/types/types/registry' { UpDataStructsProperty: UpDataStructsProperty; UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission; UpDataStructsPropertyPermission: UpDataStructsPropertyPermission; + UpDataStructsPropertyScope: UpDataStructsPropertyScope; UpDataStructsRpcCollection: UpDataStructsRpcCollection; UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; UpDataStructsSponsorshipState: UpDataStructsSponsorshipState; diff --git a/tests/src/interfaces/default/types.ts b/tests/src/interfaces/default/types.ts index 34cf9913fa..c0788206cc 100644 --- a/tests/src/interfaces/default/types.ts +++ b/tests/src/interfaces/default/types.ts @@ -31,17 +31,35 @@ export interface CumulusPalletDmpQueueError extends Enum { /** @name CumulusPalletDmpQueueEvent */ export interface CumulusPalletDmpQueueEvent extends Enum { readonly isInvalidFormat: boolean; - readonly asInvalidFormat: U8aFixed; + readonly asInvalidFormat: { + readonly messageId: U8aFixed; + } & Struct; readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: U8aFixed; + readonly asUnsupportedVersion: { + readonly messageId: U8aFixed; + } & Struct; readonly isExecutedDownward: boolean; - readonly asExecutedDownward: ITuple<[U8aFixed, XcmV2TraitsOutcome]>; + readonly asExecutedDownward: { + readonly messageId: U8aFixed; + readonly outcome: XcmV2TraitsOutcome; + } & Struct; readonly isWeightExhausted: boolean; - readonly asWeightExhausted: ITuple<[U8aFixed, u64, u64]>; + readonly asWeightExhausted: { + readonly messageId: U8aFixed; + readonly remainingWeight: u64; + readonly requiredWeight: u64; + } & Struct; readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: ITuple<[U8aFixed, u64, u64]>; + readonly asOverweightEnqueued: { + readonly messageId: U8aFixed; + readonly overweightIndex: u64; + readonly requiredWeight: u64; + } & Struct; readonly isOverweightServiced: boolean; - readonly asOverweightServiced: ITuple<[u64, u64]>; + readonly asOverweightServiced: { + readonly overweightIndex: u64; + readonly weightUsed: u64; + } & Struct; readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced'; } @@ -90,14 +108,23 @@ export interface CumulusPalletParachainSystemError extends Enum { export interface CumulusPalletParachainSystemEvent extends Enum { readonly isValidationFunctionStored: boolean; readonly isValidationFunctionApplied: boolean; - readonly asValidationFunctionApplied: u32; + readonly asValidationFunctionApplied: { + readonly relayChainBlockNum: u32; + } & Struct; readonly isValidationFunctionDiscarded: boolean; readonly isUpgradeAuthorized: boolean; - readonly asUpgradeAuthorized: H256; + readonly asUpgradeAuthorized: { + readonly codeHash: H256; + } & Struct; readonly isDownwardMessagesReceived: boolean; - readonly asDownwardMessagesReceived: u32; + readonly asDownwardMessagesReceived: { + readonly count: u32; + } & Struct; readonly isDownwardMessagesProcessed: boolean; - readonly asDownwardMessagesProcessed: ITuple<[u64, H256]>; + readonly asDownwardMessagesProcessed: { + readonly weightUsed: u64; + readonly dmqHead: H256; + } & Struct; readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed'; } @@ -535,14 +562,6 @@ export interface FrameSupportWeightsRuntimeDbWeight extends Struct { readonly write: u64; } -/** @name FrameSupportWeightsWeightToFeeCoefficient */ -export interface FrameSupportWeightsWeightToFeeCoefficient extends Struct { - readonly coeffInteger: u128; - readonly coeffFrac: Perbill; - readonly negative: bool; - readonly degree: u8; -} - /** @name FrameSystemAccountInfo */ export interface FrameSystemAccountInfo extends Struct { readonly nonce: u32; @@ -1175,9 +1194,10 @@ export interface PalletNonfungibleItemData extends Struct { export interface PalletRefungibleError extends Enum { readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isWrongRefungiblePieces: boolean; + readonly isRepartitionWhileNotOwningAllPieces: boolean; readonly isRefungibleDisallowsNesting: boolean; readonly isSettingPropertiesNotAllowed: boolean; - readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; + readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } /** @name PalletRefungibleItemData */ @@ -1208,7 +1228,7 @@ export interface PalletRmrkCoreCall extends Enum { } & Struct; readonly isMintNft: boolean; readonly asMintNft: { - readonly owner: AccountId32; + readonly owner: Option; readonly collectionId: u32; readonly recipient: Option; readonly royaltyAmount: Option; @@ -1243,13 +1263,13 @@ export interface PalletRmrkCoreCall extends Enum { readonly asAcceptResource: { readonly rmrkCollectionId: u32; readonly rmrkNftId: u32; - readonly rmrkResourceId: u32; + readonly resourceId: u32; } & Struct; readonly isAcceptResourceRemoval: boolean; readonly asAcceptResourceRemoval: { readonly rmrkCollectionId: u32; readonly rmrkNftId: u32; - readonly rmrkResourceId: u32; + readonly resourceId: u32; } & Struct; readonly isSetProperty: boolean; readonly asSetProperty: { @@ -1297,6 +1317,7 @@ export interface PalletRmrkCoreError extends Enum { readonly isNftTypeEncodeError: boolean; readonly isRmrkPropertyKeyIsTooLong: boolean; readonly isRmrkPropertyValueIsTooLong: boolean; + readonly isRmrkPropertyIsNotFound: boolean; readonly isUnableToDecodeRmrkData: boolean; readonly isCollectionNotEmpty: boolean; readonly isNoAvailableCollectionId: boolean; @@ -1309,8 +1330,10 @@ export interface PalletRmrkCoreError extends Enum { readonly isCannotSendToDescendentOrSelf: boolean; readonly isCannotAcceptNonOwnedNft: boolean; readonly isCannotRejectNonOwnedNft: boolean; + readonly isCannotRejectNonPendingNft: boolean; readonly isResourceNotPending: boolean; - readonly type: 'CorruptedCollectionType' | 'NftTypeEncodeError' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'ResourceNotPending'; + readonly isNoAvailableResourceId: boolean; + readonly type: 'CorruptedCollectionType' | 'NftTypeEncodeError' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId'; } /** @name PalletRmrkCoreEvent */ @@ -1416,7 +1439,13 @@ export interface PalletRmrkEquipCall extends Enum { readonly baseId: u32; readonly theme: RmrkTraitsTheme; } & Struct; - readonly type: 'CreateBase' | 'ThemeAdd'; + readonly isEquippable: boolean; + readonly asEquippable: { + readonly baseId: u32; + readonly slotId: u32; + readonly equippables: RmrkTraitsPartEquippableList; + } & Struct; + readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable'; } /** @name PalletRmrkEquipError */ @@ -1426,7 +1455,9 @@ export interface PalletRmrkEquipError extends Enum { readonly isNoAvailablePartId: boolean; readonly isBaseDoesntExist: boolean; readonly isNeedsDefaultThemeFirst: boolean; - readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst'; + readonly isPartDoesntExist: boolean; + readonly isNoEquippableOnFixedPart: boolean; + readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart'; } /** @name PalletRmrkEquipEvent */ @@ -1436,7 +1467,12 @@ export interface PalletRmrkEquipEvent extends Enum { readonly issuer: AccountId32; readonly baseId: u32; } & Struct; - readonly type: 'BaseCreated'; + readonly isEquippablesUpdated: boolean; + readonly asEquippablesUpdated: { + readonly baseId: u32; + readonly slotId: u32; + } & Struct; + readonly type: 'BaseCreated' | 'EquippablesUpdated'; } /** @name PalletStructureCall */ @@ -1750,7 +1786,13 @@ export interface PalletUniqueCall extends Enum { readonly collectionId: u32; readonly newLimit: UpDataStructsCollectionPermissions; } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions'; + readonly isRepartition: boolean; + readonly asRepartition: { + readonly collectionId: u32; + readonly token: u32; + readonly amount: u128; + } & Struct; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition'; } /** @name PalletUniqueError */ @@ -1758,7 +1800,8 @@ export interface PalletUniqueError extends Enum { readonly isCollectionDecimalPointLimitExceeded: boolean; readonly isConfirmUnsetSponsorFail: boolean; readonly isEmptyArgument: boolean; - readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument'; + readonly isRepartitionCalledOnNonRefungibleCollection: boolean; + readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; } /** @name PalletUniqueRawEvent */ @@ -2431,7 +2474,6 @@ export interface UpDataStructsNestingPermissions extends Struct { readonly tokenOwner: bool; readonly collectionAdmin: bool; readonly restricted: Option; - readonly permissive: bool; } /** @name UpDataStructsOwnerRestrictedSet */ @@ -2469,6 +2511,13 @@ export interface UpDataStructsPropertyPermission extends Struct { readonly tokenOwner: bool; } +/** @name UpDataStructsPropertyScope */ +export interface UpDataStructsPropertyScope extends Enum { + readonly isNone: boolean; + readonly isRmrk: boolean; + readonly type: 'None' | 'Rmrk'; +} + /** @name UpDataStructsRpcCollection */ export interface UpDataStructsRpcCollection extends Struct { readonly owner: AccountId32; diff --git a/tests/src/interfaces/lookup.ts b/tests/src/interfaces/lookup.ts index 1598e4191d..52a0c267e4 100644 --- a/tests/src/interfaces/lookup.ts +++ b/tests/src/interfaces/lookup.ts @@ -114,11 +114,20 @@ export default { CumulusPalletParachainSystemEvent: { _enum: { ValidationFunctionStored: 'Null', - ValidationFunctionApplied: 'u32', + ValidationFunctionApplied: { + relayChainBlockNum: 'u32', + }, ValidationFunctionDiscarded: 'Null', - UpgradeAuthorized: 'H256', - DownwardMessagesReceived: 'u32', - DownwardMessagesProcessed: '(u64,H256)' + UpgradeAuthorized: { + codeHash: 'H256', + }, + DownwardMessagesReceived: { + count: 'u32', + }, + DownwardMessagesProcessed: { + weightUsed: 'u64', + dmqHead: 'H256' + } } }, /** @@ -276,16 +285,7 @@ export default { _enum: ['V1Ancient', 'V2'] }, /** - * Lookup68: frame_support::weights::WeightToFeeCoefficient - **/ - FrameSupportWeightsWeightToFeeCoefficient: { - coeffInteger: 'u128', - coeffFrac: 'Perbill', - negative: 'bool', - degree: 'u8' - }, - /** - * Lookup70: pallet_treasury::Proposal + * Lookup67: pallet_treasury::Proposal **/ PalletTreasuryProposal: { proposer: 'AccountId32', @@ -294,7 +294,7 @@ export default { bond: 'u128' }, /** - * Lookup73: pallet_treasury::pallet::Call + * Lookup70: pallet_treasury::pallet::Call **/ PalletTreasuryCall: { _enum: { @@ -314,7 +314,7 @@ export default { } }, /** - * Lookup75: pallet_treasury::pallet::Event + * Lookup72: pallet_treasury::pallet::Event **/ PalletTreasuryEvent: { _enum: { @@ -345,17 +345,17 @@ export default { } }, /** - * Lookup78: frame_support::PalletId + * Lookup75: frame_support::PalletId **/ FrameSupportPalletId: '[u8;8]', /** - * Lookup79: pallet_treasury::pallet::Error + * Lookup76: pallet_treasury::pallet::Error **/ PalletTreasuryError: { _enum: ['InsufficientProposersBalance', 'InvalidIndex', 'TooManyApprovals', 'ProposalNotApproved'] }, /** - * Lookup80: pallet_sudo::pallet::Call + * Lookup77: pallet_sudo::pallet::Call **/ PalletSudoCall: { _enum: { @@ -379,7 +379,7 @@ export default { } }, /** - * Lookup82: frame_system::pallet::Call + * Lookup79: frame_system::pallet::Call **/ FrameSystemCall: { _enum: { @@ -417,7 +417,7 @@ export default { } }, /** - * Lookup85: orml_vesting::module::Call + * Lookup83: orml_vesting::module::Call **/ OrmlVestingModuleCall: { _enum: { @@ -436,7 +436,7 @@ export default { } }, /** - * Lookup86: orml_vesting::VestingSchedule + * Lookup84: orml_vesting::VestingSchedule **/ OrmlVestingVestingSchedule: { start: 'u32', @@ -445,7 +445,7 @@ export default { perPeriod: 'Compact' }, /** - * Lookup88: cumulus_pallet_xcmp_queue::pallet::Call + * Lookup86: cumulus_pallet_xcmp_queue::pallet::Call **/ CumulusPalletXcmpQueueCall: { _enum: { @@ -494,7 +494,7 @@ export default { } }, /** - * Lookup89: pallet_xcm::pallet::Call + * Lookup87: pallet_xcm::pallet::Call **/ PalletXcmCall: { _enum: { @@ -548,7 +548,7 @@ export default { } }, /** - * Lookup90: xcm::VersionedMultiLocation + * Lookup88: xcm::VersionedMultiLocation **/ XcmVersionedMultiLocation: { _enum: { @@ -557,7 +557,7 @@ export default { } }, /** - * Lookup91: xcm::v0::multi_location::MultiLocation + * Lookup89: xcm::v0::multi_location::MultiLocation **/ XcmV0MultiLocation: { _enum: { @@ -573,7 +573,7 @@ export default { } }, /** - * Lookup92: xcm::v0::junction::Junction + * Lookup90: xcm::v0::junction::Junction **/ XcmV0Junction: { _enum: { @@ -602,7 +602,7 @@ export default { } }, /** - * Lookup93: xcm::v0::junction::NetworkId + * Lookup91: xcm::v0::junction::NetworkId **/ XcmV0JunctionNetworkId: { _enum: { @@ -613,7 +613,7 @@ export default { } }, /** - * Lookup94: xcm::v0::junction::BodyId + * Lookup92: xcm::v0::junction::BodyId **/ XcmV0JunctionBodyId: { _enum: { @@ -627,7 +627,7 @@ export default { } }, /** - * Lookup95: xcm::v0::junction::BodyPart + * Lookup93: xcm::v0::junction::BodyPart **/ XcmV0JunctionBodyPart: { _enum: { @@ -650,14 +650,14 @@ export default { } }, /** - * Lookup96: xcm::v1::multilocation::MultiLocation + * Lookup94: xcm::v1::multilocation::MultiLocation **/ XcmV1MultiLocation: { parents: 'u8', interior: 'XcmV1MultilocationJunctions' }, /** - * Lookup97: xcm::v1::multilocation::Junctions + * Lookup95: xcm::v1::multilocation::Junctions **/ XcmV1MultilocationJunctions: { _enum: { @@ -673,7 +673,7 @@ export default { } }, /** - * Lookup98: xcm::v1::junction::Junction + * Lookup96: xcm::v1::junction::Junction **/ XcmV1Junction: { _enum: { @@ -701,7 +701,7 @@ export default { } }, /** - * Lookup99: xcm::VersionedXcm + * Lookup97: xcm::VersionedXcm **/ XcmVersionedXcm: { _enum: { @@ -711,7 +711,7 @@ export default { } }, /** - * Lookup100: xcm::v0::Xcm + * Lookup98: xcm::v0::Xcm **/ XcmV0Xcm: { _enum: { @@ -765,7 +765,7 @@ export default { } }, /** - * Lookup102: xcm::v0::multi_asset::MultiAsset + * Lookup100: xcm::v0::multi_asset::MultiAsset **/ XcmV0MultiAsset: { _enum: { @@ -804,7 +804,7 @@ export default { } }, /** - * Lookup103: xcm::v1::multiasset::AssetInstance + * Lookup101: xcm::v1::multiasset::AssetInstance **/ XcmV1MultiassetAssetInstance: { _enum: { @@ -818,7 +818,7 @@ export default { } }, /** - * Lookup106: xcm::v0::order::Order + * Lookup104: xcm::v0::order::Order **/ XcmV0Order: { _enum: { @@ -861,7 +861,7 @@ export default { } }, /** - * Lookup108: xcm::v0::Response + * Lookup106: xcm::v0::Response **/ XcmV0Response: { _enum: { @@ -869,19 +869,19 @@ export default { } }, /** - * Lookup109: xcm::v0::OriginKind + * Lookup107: xcm::v0::OriginKind **/ XcmV0OriginKind: { _enum: ['Native', 'SovereignAccount', 'Superuser', 'Xcm'] }, /** - * Lookup110: xcm::double_encoded::DoubleEncoded + * Lookup108: xcm::double_encoded::DoubleEncoded **/ XcmDoubleEncoded: { encoded: 'Bytes' }, /** - * Lookup111: xcm::v1::Xcm + * Lookup109: xcm::v1::Xcm **/ XcmV1Xcm: { _enum: { @@ -940,18 +940,18 @@ export default { } }, /** - * Lookup112: xcm::v1::multiasset::MultiAssets + * Lookup110: xcm::v1::multiasset::MultiAssets **/ XcmV1MultiassetMultiAssets: 'Vec', /** - * Lookup114: xcm::v1::multiasset::MultiAsset + * Lookup112: xcm::v1::multiasset::MultiAsset **/ XcmV1MultiAsset: { id: 'XcmV1MultiassetAssetId', fun: 'XcmV1MultiassetFungibility' }, /** - * Lookup115: xcm::v1::multiasset::AssetId + * Lookup113: xcm::v1::multiasset::AssetId **/ XcmV1MultiassetAssetId: { _enum: { @@ -960,7 +960,7 @@ export default { } }, /** - * Lookup116: xcm::v1::multiasset::Fungibility + * Lookup114: xcm::v1::multiasset::Fungibility **/ XcmV1MultiassetFungibility: { _enum: { @@ -969,7 +969,7 @@ export default { } }, /** - * Lookup118: xcm::v1::order::Order + * Lookup116: xcm::v1::order::Order **/ XcmV1Order: { _enum: { @@ -1014,7 +1014,7 @@ export default { } }, /** - * Lookup119: xcm::v1::multiasset::MultiAssetFilter + * Lookup117: xcm::v1::multiasset::MultiAssetFilter **/ XcmV1MultiassetMultiAssetFilter: { _enum: { @@ -1023,7 +1023,7 @@ export default { } }, /** - * Lookup120: xcm::v1::multiasset::WildMultiAsset + * Lookup118: xcm::v1::multiasset::WildMultiAsset **/ XcmV1MultiassetWildMultiAsset: { _enum: { @@ -1035,13 +1035,13 @@ export default { } }, /** - * Lookup121: xcm::v1::multiasset::WildFungibility + * Lookup119: xcm::v1::multiasset::WildFungibility **/ XcmV1MultiassetWildFungibility: { _enum: ['Fungible', 'NonFungible'] }, /** - * Lookup123: xcm::v1::Response + * Lookup121: xcm::v1::Response **/ XcmV1Response: { _enum: { @@ -1050,11 +1050,11 @@ export default { } }, /** - * Lookup124: xcm::v2::Xcm + * Lookup122: xcm::v2::Xcm **/ XcmV2Xcm: 'Vec', /** - * Lookup126: xcm::v2::Instruction + * Lookup124: xcm::v2::Instruction **/ XcmV2Instruction: { _enum: { @@ -1152,7 +1152,7 @@ export default { } }, /** - * Lookup127: xcm::v2::Response + * Lookup125: xcm::v2::Response **/ XcmV2Response: { _enum: { @@ -1163,7 +1163,7 @@ export default { } }, /** - * Lookup130: xcm::v2::traits::Error + * Lookup128: xcm::v2::traits::Error **/ XcmV2TraitsError: { _enum: { @@ -1196,7 +1196,7 @@ export default { } }, /** - * Lookup131: xcm::v2::WeightLimit + * Lookup129: xcm::v2::WeightLimit **/ XcmV2WeightLimit: { _enum: { @@ -1205,7 +1205,7 @@ export default { } }, /** - * Lookup132: xcm::VersionedMultiAssets + * Lookup130: xcm::VersionedMultiAssets **/ XcmVersionedMultiAssets: { _enum: { @@ -1214,11 +1214,11 @@ export default { } }, /** - * Lookup147: cumulus_pallet_xcm::pallet::Call + * Lookup145: cumulus_pallet_xcm::pallet::Call **/ CumulusPalletXcmCall: 'Null', /** - * Lookup148: cumulus_pallet_dmp_queue::pallet::Call + * Lookup146: cumulus_pallet_dmp_queue::pallet::Call **/ CumulusPalletDmpQueueCall: { _enum: { @@ -1229,7 +1229,7 @@ export default { } }, /** - * Lookup149: pallet_inflation::pallet::Call + * Lookup147: pallet_inflation::pallet::Call **/ PalletInflationCall: { _enum: { @@ -1239,7 +1239,7 @@ export default { } }, /** - * Lookup150: pallet_unique::Call + * Lookup148: pallet_unique::Call **/ PalletUniqueCall: { _enum: { @@ -1361,12 +1361,17 @@ export default { }, set_collection_permissions: { collectionId: 'u32', - newLimit: 'UpDataStructsCollectionPermissions' + newLimit: 'UpDataStructsCollectionPermissions', + }, + repartition: { + collectionId: 'u32', + token: 'u32', + amount: 'u128' } } }, /** - * Lookup156: up_data_structs::CollectionMode + * Lookup154: up_data_structs::CollectionMode **/ UpDataStructsCollectionMode: { _enum: { @@ -1376,7 +1381,7 @@ export default { } }, /** - * Lookup157: up_data_structs::CreateCollectionData + * Lookup155: up_data_structs::CreateCollectionData **/ UpDataStructsCreateCollectionData: { mode: 'UpDataStructsCollectionMode', @@ -1391,13 +1396,13 @@ export default { properties: 'Vec' }, /** - * Lookup159: up_data_structs::AccessMode + * Lookup157: up_data_structs::AccessMode **/ UpDataStructsAccessMode: { _enum: ['Normal', 'AllowList'] }, /** - * Lookup162: up_data_structs::CollectionLimits + * Lookup160: up_data_structs::CollectionLimits **/ UpDataStructsCollectionLimits: { accountTokenOwnershipLimit: 'Option', @@ -1411,7 +1416,7 @@ export default { transfersEnabled: 'Option' }, /** - * Lookup164: up_data_structs::SponsoringRateLimit + * Lookup162: up_data_structs::SponsoringRateLimit **/ UpDataStructsSponsoringRateLimit: { _enum: { @@ -1420,7 +1425,7 @@ export default { } }, /** - * Lookup167: up_data_structs::CollectionPermissions + * Lookup165: up_data_structs::CollectionPermissions **/ UpDataStructsCollectionPermissions: { access: 'Option', @@ -1428,27 +1433,26 @@ export default { nesting: 'Option' }, /** - * Lookup169: up_data_structs::NestingPermissions + * Lookup167: up_data_structs::NestingPermissions **/ UpDataStructsNestingPermissions: { tokenOwner: 'bool', collectionAdmin: 'bool', - restricted: 'Option', - permissive: 'bool' + restricted: 'Option' }, /** - * Lookup171: up_data_structs::OwnerRestrictedSet + * Lookup169: up_data_structs::OwnerRestrictedSet **/ UpDataStructsOwnerRestrictedSet: 'BTreeSet', /** - * Lookup177: up_data_structs::PropertyKeyPermission + * Lookup175: up_data_structs::PropertyKeyPermission **/ UpDataStructsPropertyKeyPermission: { key: 'Bytes', permission: 'UpDataStructsPropertyPermission' }, /** - * Lookup179: up_data_structs::PropertyPermission + * Lookup177: up_data_structs::PropertyPermission **/ UpDataStructsPropertyPermission: { mutable: 'bool', @@ -1456,14 +1460,14 @@ export default { tokenOwner: 'bool' }, /** - * Lookup182: up_data_structs::Property + * Lookup180: up_data_structs::Property **/ UpDataStructsProperty: { key: 'Bytes', value: 'Bytes' }, /** - * Lookup185: pallet_evm::account::BasicCrossAccountIdRepr + * Lookup183: pallet_evm::account::BasicCrossAccountIdRepr **/ PalletEvmAccountBasicCrossAccountIdRepr: { _enum: { @@ -1472,7 +1476,7 @@ export default { } }, /** - * Lookup187: up_data_structs::CreateItemData + * Lookup185: up_data_structs::CreateItemData **/ UpDataStructsCreateItemData: { _enum: { @@ -1482,26 +1486,26 @@ export default { } }, /** - * Lookup188: up_data_structs::CreateNftData + * Lookup186: up_data_structs::CreateNftData **/ UpDataStructsCreateNftData: { properties: 'Vec' }, /** - * Lookup189: up_data_structs::CreateFungibleData + * Lookup187: up_data_structs::CreateFungibleData **/ UpDataStructsCreateFungibleData: { value: 'u128' }, /** - * Lookup190: up_data_structs::CreateReFungibleData + * Lookup188: up_data_structs::CreateReFungibleData **/ UpDataStructsCreateReFungibleData: { constData: 'Bytes', pieces: 'u128' }, /** - * Lookup195: up_data_structs::CreateItemExData> + * Lookup193: up_data_structs::CreateItemExData> **/ UpDataStructsCreateItemExData: { _enum: { @@ -1512,21 +1516,21 @@ export default { } }, /** - * Lookup197: up_data_structs::CreateNftExData> + * Lookup195: up_data_structs::CreateNftExData> **/ UpDataStructsCreateNftExData: { properties: 'Vec', owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup204: up_data_structs::CreateRefungibleExData> + * Lookup202: up_data_structs::CreateRefungibleExData> **/ UpDataStructsCreateRefungibleExData: { constData: 'Bytes', users: 'BTreeMap' }, /** - * Lookup206: pallet_unique_scheduler::pallet::Call + * Lookup204: pallet_unique_scheduler::pallet::Call **/ PalletUniqueSchedulerCall: { _enum: { @@ -1550,7 +1554,7 @@ export default { } }, /** - * Lookup208: frame_support::traits::schedule::MaybeHashed + * Lookup206: frame_support::traits::schedule::MaybeHashed **/ FrameSupportScheduleMaybeHashed: { _enum: { @@ -1559,15 +1563,15 @@ export default { } }, /** - * Lookup209: pallet_template_transaction_payment::Call + * Lookup207: pallet_template_transaction_payment::Call **/ PalletTemplateTransactionPaymentCall: 'Null', /** - * Lookup210: pallet_structure::pallet::Call + * Lookup208: pallet_structure::pallet::Call **/ PalletStructureCall: 'Null', /** - * Lookup211: pallet_rmrk_core::pallet::Call + * Lookup209: pallet_rmrk_core::pallet::Call **/ PalletRmrkCoreCall: { _enum: { @@ -1587,7 +1591,7 @@ export default { collectionId: 'u32', }, mint_nft: { - owner: 'AccountId32', + owner: 'Option', collectionId: 'u32', recipient: 'Option', royaltyAmount: 'Option', @@ -1617,12 +1621,12 @@ export default { accept_resource: { rmrkCollectionId: 'u32', rmrkNftId: 'u32', - rmrkResourceId: 'u32', + resourceId: 'u32', }, accept_resource_removal: { rmrkCollectionId: 'u32', rmrkNftId: 'u32', - rmrkResourceId: 'u32', + resourceId: 'u32', }, set_property: { rmrkCollectionId: 'Compact', @@ -1658,7 +1662,7 @@ export default { } }, /** - * Lookup217: rmrk_traits::resource::ResourceTypes, frame_support::storage::bounded_vec::BoundedVec> + * Lookup215: rmrk_traits::resource::ResourceTypes, frame_support::storage::bounded_vec::BoundedVec> **/ RmrkTraitsResourceResourceTypes: { _enum: { @@ -1668,7 +1672,7 @@ export default { } }, /** - * Lookup219: rmrk_traits::resource::BasicResource> + * Lookup217: rmrk_traits::resource::BasicResource> **/ RmrkTraitsResourceBasicResource: { src: 'Option', @@ -1677,7 +1681,7 @@ export default { thumb: 'Option' }, /** - * Lookup221: rmrk_traits::resource::ComposableResource, frame_support::storage::bounded_vec::BoundedVec> + * Lookup219: rmrk_traits::resource::ComposableResource, frame_support::storage::bounded_vec::BoundedVec> **/ RmrkTraitsResourceComposableResource: { parts: 'Vec', @@ -1688,7 +1692,7 @@ export default { thumb: 'Option' }, /** - * Lookup222: rmrk_traits::resource::SlotResource> + * Lookup220: rmrk_traits::resource::SlotResource> **/ RmrkTraitsResourceSlotResource: { base: 'u32', @@ -1699,7 +1703,7 @@ export default { thumb: 'Option' }, /** - * Lookup224: rmrk_traits::nft::AccountIdOrCollectionNftTuple + * Lookup222: rmrk_traits::nft::AccountIdOrCollectionNftTuple **/ RmrkTraitsNftAccountIdOrCollectionNftTuple: { _enum: { @@ -1708,7 +1712,7 @@ export default { } }, /** - * Lookup228: pallet_rmrk_equip::pallet::Call + * Lookup226: pallet_rmrk_equip::pallet::Call **/ PalletRmrkEquipCall: { _enum: { @@ -1719,12 +1723,17 @@ export default { }, theme_add: { baseId: 'u32', - theme: 'RmrkTraitsTheme' + theme: 'RmrkTraitsTheme', + }, + equippable: { + baseId: 'u32', + slotId: 'u32', + equippables: 'RmrkTraitsPartEquippableList' } } }, /** - * Lookup230: rmrk_traits::part::PartType, frame_support::storage::bounded_vec::BoundedVec> + * Lookup229: rmrk_traits::part::PartType, frame_support::storage::bounded_vec::BoundedVec> **/ RmrkTraitsPartPartType: { _enum: { @@ -1733,7 +1742,7 @@ export default { } }, /** - * Lookup232: rmrk_traits::part::FixedPart> + * Lookup231: rmrk_traits::part::FixedPart> **/ RmrkTraitsPartFixedPart: { id: 'u32', @@ -1741,7 +1750,7 @@ export default { src: 'Bytes' }, /** - * Lookup233: rmrk_traits::part::SlotPart, frame_support::storage::bounded_vec::BoundedVec> + * Lookup232: rmrk_traits::part::SlotPart, frame_support::storage::bounded_vec::BoundedVec> **/ RmrkTraitsPartSlotPart: { id: 'u32', @@ -1750,7 +1759,7 @@ export default { z: 'u32' }, /** - * Lookup234: rmrk_traits::part::EquippableList> + * Lookup233: rmrk_traits::part::EquippableList> **/ RmrkTraitsPartEquippableList: { _enum: { @@ -1760,7 +1769,7 @@ export default { } }, /** - * Lookup236: rmrk_traits::theme::Theme, PropertyList> + * Lookup235: rmrk_traits::theme::Theme, frame_support::storage::bounded_vec::BoundedVec>, S>> **/ RmrkTraitsTheme: { name: 'Bytes', @@ -1768,7 +1777,7 @@ export default { inherit: 'bool' }, /** - * Lookup238: rmrk_traits::theme::ThemeProperty> + * Lookup237: rmrk_traits::theme::ThemeProperty> **/ RmrkTraitsThemeThemeProperty: { key: 'Bytes', @@ -2166,12 +2175,30 @@ export default { **/ CumulusPalletDmpQueueEvent: { _enum: { - InvalidFormat: '[u8;32]', - UnsupportedVersion: '[u8;32]', - ExecutedDownward: '([u8;32],XcmV2TraitsOutcome)', - WeightExhausted: '([u8;32],u64,u64)', - OverweightEnqueued: '([u8;32],u64,u64)', - OverweightServiced: '(u64,u64)' + InvalidFormat: { + messageId: '[u8;32]', + }, + UnsupportedVersion: { + messageId: '[u8;32]', + }, + ExecutedDownward: { + messageId: '[u8;32]', + outcome: 'XcmV2TraitsOutcome', + }, + WeightExhausted: { + messageId: '[u8;32]', + remainingWeight: 'u64', + requiredWeight: 'u64', + }, + OverweightEnqueued: { + messageId: '[u8;32]', + overweightIndex: 'u64', + requiredWeight: 'u64', + }, + OverweightServiced: { + overweightIndex: 'u64', + weightUsed: 'u64' + } } }, /** @@ -2332,7 +2359,11 @@ export default { _enum: { BaseCreated: { issuer: 'AccountId32', - baseId: 'u32' + baseId: 'u32', + }, + EquippablesUpdated: { + baseId: 'u32', + slotId: 'u32' } } }, @@ -2597,7 +2628,7 @@ export default { * Lookup344: pallet_unique::Error **/ PalletUniqueError: { - _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument'] + _enum: ['CollectionDecimalPointLimitExceeded', 'ConfirmUnsetSponsorFail', 'EmptyArgument', 'RepartitionCalledOnNonRefungibleCollection'] }, /** * Lookup347: pallet_unique_scheduler::ScheduledV3, BlockNumber, opal_runtime::OriginCaller, sp_core::crypto::AccountId32> @@ -2926,7 +2957,7 @@ export default { * Lookup393: pallet_refungible::pallet::Error **/ PalletRefungibleError: { - _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] + _enum: ['NotRefungibleDataUsedToMintFungibleCollectionToken', 'WrongRefungiblePieces', 'RepartitionWhileNotOwningAllPieces', 'RefungibleDisallowsNesting', 'SettingPropertiesNotAllowed'] }, /** * Lookup394: pallet_nonfungible::ItemData> @@ -2935,37 +2966,43 @@ export default { owner: 'PalletEvmAccountBasicCrossAccountIdRepr' }, /** - * Lookup396: pallet_nonfungible::pallet::Error + * Lookup396: up_data_structs::PropertyScope + **/ + UpDataStructsPropertyScope: { + _enum: ['None', 'Rmrk'] + }, + /** + * Lookup398: pallet_nonfungible::pallet::Error **/ PalletNonfungibleError: { _enum: ['NotNonfungibleDataUsedToMintFungibleCollectionToken', 'NonfungibleItemsHaveNoAmount', 'CantBurnNftWithChildren'] }, /** - * Lookup397: pallet_structure::pallet::Error + * Lookup399: pallet_structure::pallet::Error **/ PalletStructureError: { _enum: ['OuroborosDetected', 'DepthLimit', 'BreadthLimit', 'TokenNotFound'] }, /** - * Lookup398: pallet_rmrk_core::pallet::Error + * Lookup400: pallet_rmrk_core::pallet::Error **/ PalletRmrkCoreError: { - _enum: ['CorruptedCollectionType', 'NftTypeEncodeError', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'ResourceNotPending'] + _enum: ['CorruptedCollectionType', 'NftTypeEncodeError', 'RmrkPropertyKeyIsTooLong', 'RmrkPropertyValueIsTooLong', 'RmrkPropertyIsNotFound', 'UnableToDecodeRmrkData', 'CollectionNotEmpty', 'NoAvailableCollectionId', 'NoAvailableNftId', 'CollectionUnknown', 'NoPermission', 'NonTransferable', 'CollectionFullOrLocked', 'ResourceDoesntExist', 'CannotSendToDescendentOrSelf', 'CannotAcceptNonOwnedNft', 'CannotRejectNonOwnedNft', 'CannotRejectNonPendingNft', 'ResourceNotPending', 'NoAvailableResourceId'] }, /** - * Lookup400: pallet_rmrk_equip::pallet::Error + * Lookup402: pallet_rmrk_equip::pallet::Error **/ PalletRmrkEquipError: { - _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst'] + _enum: ['PermissionError', 'NoAvailableBaseId', 'NoAvailablePartId', 'BaseDoesntExist', 'NeedsDefaultThemeFirst', 'PartDoesntExist', 'NoEquippableOnFixedPart'] }, /** - * Lookup403: pallet_evm::pallet::Error + * Lookup405: pallet_evm::pallet::Error **/ PalletEvmError: { _enum: ['BalanceLow', 'FeeOverflow', 'PaymentOverflow', 'WithdrawFailed', 'GasPriceTooLow', 'InvalidNonce'] }, /** - * Lookup406: fp_rpc::TransactionStatus + * Lookup408: fp_rpc::TransactionStatus **/ FpRpcTransactionStatus: { transactionHash: 'H256', @@ -2977,11 +3014,11 @@ export default { logsBloom: 'EthbloomBloom' }, /** - * Lookup408: ethbloom::Bloom + * Lookup410: ethbloom::Bloom **/ EthbloomBloom: '[u8;256]', /** - * Lookup410: ethereum::receipt::ReceiptV3 + * Lookup412: ethereum::receipt::ReceiptV3 **/ EthereumReceiptReceiptV3: { _enum: { @@ -2991,7 +3028,7 @@ export default { } }, /** - * Lookup411: ethereum::receipt::EIP658ReceiptData + * Lookup413: ethereum::receipt::EIP658ReceiptData **/ EthereumReceiptEip658ReceiptData: { statusCode: 'u8', @@ -3000,7 +3037,7 @@ export default { logs: 'Vec' }, /** - * Lookup412: ethereum::block::Block + * Lookup414: ethereum::block::Block **/ EthereumBlock: { header: 'EthereumHeader', @@ -3008,7 +3045,7 @@ export default { ommers: 'Vec' }, /** - * Lookup413: ethereum::header::Header + * Lookup415: ethereum::header::Header **/ EthereumHeader: { parentHash: 'H256', @@ -3028,41 +3065,41 @@ export default { nonce: 'EthereumTypesHashH64' }, /** - * Lookup414: ethereum_types::hash::H64 + * Lookup416: ethereum_types::hash::H64 **/ EthereumTypesHashH64: '[u8;8]', /** - * Lookup419: pallet_ethereum::pallet::Error + * Lookup421: pallet_ethereum::pallet::Error **/ PalletEthereumError: { _enum: ['InvalidSignature', 'PreLogExists'] }, /** - * Lookup420: pallet_evm_coder_substrate::pallet::Error + * Lookup422: pallet_evm_coder_substrate::pallet::Error **/ PalletEvmCoderSubstrateError: { _enum: ['OutOfGas', 'OutOfFund'] }, /** - * Lookup421: pallet_evm_contract_helpers::SponsoringModeT + * Lookup423: pallet_evm_contract_helpers::SponsoringModeT **/ PalletEvmContractHelpersSponsoringModeT: { _enum: ['Disabled', 'Allowlisted', 'Generous'] }, /** - * Lookup423: pallet_evm_contract_helpers::pallet::Error + * Lookup425: pallet_evm_contract_helpers::pallet::Error **/ PalletEvmContractHelpersError: { _enum: ['NoPermission'] }, /** - * Lookup424: pallet_evm_migration::pallet::Error + * Lookup426: pallet_evm_migration::pallet::Error **/ PalletEvmMigrationError: { _enum: ['AccountNotEmpty', 'AccountIsNotMigrating'] }, /** - * Lookup426: sp_runtime::MultiSignature + * Lookup428: sp_runtime::MultiSignature **/ SpRuntimeMultiSignature: { _enum: { @@ -3072,43 +3109,43 @@ export default { } }, /** - * Lookup427: sp_core::ed25519::Signature + * Lookup429: sp_core::ed25519::Signature **/ SpCoreEd25519Signature: '[u8;64]', /** - * Lookup429: sp_core::sr25519::Signature + * Lookup431: sp_core::sr25519::Signature **/ SpCoreSr25519Signature: '[u8;64]', /** - * Lookup430: sp_core::ecdsa::Signature + * Lookup432: sp_core::ecdsa::Signature **/ SpCoreEcdsaSignature: '[u8;65]', /** - * Lookup433: frame_system::extensions::check_spec_version::CheckSpecVersion + * Lookup435: frame_system::extensions::check_spec_version::CheckSpecVersion **/ FrameSystemExtensionsCheckSpecVersion: 'Null', /** - * Lookup434: frame_system::extensions::check_genesis::CheckGenesis + * Lookup436: frame_system::extensions::check_genesis::CheckGenesis **/ FrameSystemExtensionsCheckGenesis: 'Null', /** - * Lookup437: frame_system::extensions::check_nonce::CheckNonce + * Lookup439: frame_system::extensions::check_nonce::CheckNonce **/ FrameSystemExtensionsCheckNonce: 'Compact', /** - * Lookup438: frame_system::extensions::check_weight::CheckWeight + * Lookup440: frame_system::extensions::check_weight::CheckWeight **/ FrameSystemExtensionsCheckWeight: 'Null', /** - * Lookup439: pallet_template_transaction_payment::ChargeTransactionPayment + * Lookup441: pallet_template_transaction_payment::ChargeTransactionPayment **/ PalletTemplateTransactionPaymentChargeTransactionPayment: 'Compact', /** - * Lookup440: opal_runtime::Runtime + * Lookup442: opal_runtime::Runtime **/ OpalRuntimeRuntime: 'Null', /** - * Lookup441: pallet_ethereum::FakeTransactionFinalizer + * Lookup443: pallet_ethereum::FakeTransactionFinalizer **/ PalletEthereumFakeTransactionFinalizer: 'Null' }; diff --git a/tests/src/interfaces/registry.ts b/tests/src/interfaces/registry.ts index 44e1bf89c5..18e938ce8f 100644 --- a/tests/src/interfaces/registry.ts +++ b/tests/src/interfaces/registry.ts @@ -1,7 +1,7 @@ // Auto-generated via `yarn polkadot-types-from-defs`, do not edit /* eslint-disable */ -import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSupportWeightsWeightToFeeCoefficient, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; +import type { CumulusPalletDmpQueueCall, CumulusPalletDmpQueueConfigData, CumulusPalletDmpQueueError, CumulusPalletDmpQueueEvent, CumulusPalletDmpQueuePageIndexData, CumulusPalletParachainSystemCall, CumulusPalletParachainSystemError, CumulusPalletParachainSystemEvent, CumulusPalletParachainSystemRelayStateSnapshotMessagingStateSnapshot, CumulusPalletXcmCall, CumulusPalletXcmError, CumulusPalletXcmEvent, CumulusPalletXcmOrigin, CumulusPalletXcmpQueueCall, CumulusPalletXcmpQueueError, CumulusPalletXcmpQueueEvent, CumulusPalletXcmpQueueInboundChannelDetails, CumulusPalletXcmpQueueInboundState, CumulusPalletXcmpQueueOutboundChannelDetails, CumulusPalletXcmpQueueOutboundState, CumulusPalletXcmpQueueQueueConfigData, CumulusPrimitivesParachainInherentParachainInherentData, EthbloomBloom, EthereumBlock, EthereumHeader, EthereumLog, EthereumReceiptEip658ReceiptData, EthereumReceiptReceiptV3, EthereumTransactionAccessListItem, EthereumTransactionEip1559Transaction, EthereumTransactionEip2930Transaction, EthereumTransactionLegacyTransaction, EthereumTransactionTransactionAction, EthereumTransactionTransactionSignature, EthereumTransactionTransactionV2, EthereumTypesHashH64, EvmCoreErrorExitError, EvmCoreErrorExitFatal, EvmCoreErrorExitReason, EvmCoreErrorExitRevert, EvmCoreErrorExitSucceed, FpRpcTransactionStatus, FrameSupportDispatchRawOrigin, FrameSupportPalletId, FrameSupportScheduleLookupError, FrameSupportScheduleMaybeHashed, FrameSupportTokensMiscBalanceStatus, FrameSupportWeightsDispatchClass, FrameSupportWeightsDispatchInfo, FrameSupportWeightsPays, FrameSupportWeightsPerDispatchClassU32, FrameSupportWeightsPerDispatchClassU64, FrameSupportWeightsPerDispatchClassWeightsPerClass, FrameSupportWeightsRuntimeDbWeight, FrameSystemAccountInfo, FrameSystemCall, FrameSystemError, FrameSystemEvent, FrameSystemEventRecord, FrameSystemExtensionsCheckGenesis, FrameSystemExtensionsCheckNonce, FrameSystemExtensionsCheckSpecVersion, FrameSystemExtensionsCheckWeight, FrameSystemLastRuntimeUpgradeInfo, FrameSystemLimitsBlockLength, FrameSystemLimitsBlockWeights, FrameSystemLimitsWeightsPerClass, FrameSystemPhase, OpalRuntimeOriginCaller, OpalRuntimeRuntime, OrmlVestingModuleCall, OrmlVestingModuleError, OrmlVestingModuleEvent, OrmlVestingVestingSchedule, PalletBalancesAccountData, PalletBalancesBalanceLock, PalletBalancesCall, PalletBalancesError, PalletBalancesEvent, PalletBalancesReasons, PalletBalancesReleases, PalletBalancesReserveData, PalletCommonError, PalletCommonEvent, PalletEthereumCall, PalletEthereumError, PalletEthereumEvent, PalletEthereumFakeTransactionFinalizer, PalletEthereumRawOrigin, PalletEvmAccountBasicCrossAccountIdRepr, PalletEvmCall, PalletEvmCoderSubstrateError, PalletEvmContractHelpersError, PalletEvmContractHelpersSponsoringModeT, PalletEvmError, PalletEvmEvent, PalletEvmMigrationCall, PalletEvmMigrationError, PalletFungibleError, PalletInflationCall, PalletNonfungibleError, PalletNonfungibleItemData, PalletRefungibleError, PalletRefungibleItemData, PalletRmrkCoreCall, PalletRmrkCoreError, PalletRmrkCoreEvent, PalletRmrkEquipCall, PalletRmrkEquipError, PalletRmrkEquipEvent, PalletStructureCall, PalletStructureError, PalletStructureEvent, PalletSudoCall, PalletSudoError, PalletSudoEvent, PalletTemplateTransactionPaymentCall, PalletTemplateTransactionPaymentChargeTransactionPayment, PalletTimestampCall, PalletTransactionPaymentReleases, PalletTreasuryCall, PalletTreasuryError, PalletTreasuryEvent, PalletTreasuryProposal, PalletUniqueCall, PalletUniqueError, PalletUniqueRawEvent, PalletUniqueSchedulerCall, PalletUniqueSchedulerError, PalletUniqueSchedulerEvent, PalletUniqueSchedulerScheduledV3, PalletXcmCall, PalletXcmError, PalletXcmEvent, PalletXcmOrigin, PhantomTypeUpDataStructs, PolkadotCorePrimitivesInboundDownwardMessage, PolkadotCorePrimitivesInboundHrmpMessage, PolkadotCorePrimitivesOutboundHrmpMessage, PolkadotParachainPrimitivesXcmpMessageFormat, PolkadotPrimitivesV2AbridgedHostConfiguration, PolkadotPrimitivesV2AbridgedHrmpChannel, PolkadotPrimitivesV2PersistedValidationData, PolkadotPrimitivesV2UpgradeRestriction, RmrkTraitsBaseBaseInfo, RmrkTraitsCollectionCollectionInfo, RmrkTraitsNftAccountIdOrCollectionNftTuple, RmrkTraitsNftNftChild, RmrkTraitsNftNftInfo, RmrkTraitsNftRoyaltyInfo, RmrkTraitsPartEquippableList, RmrkTraitsPartFixedPart, RmrkTraitsPartPartType, RmrkTraitsPartSlotPart, RmrkTraitsPropertyPropertyInfo, RmrkTraitsResourceBasicResource, RmrkTraitsResourceComposableResource, RmrkTraitsResourceResourceInfo, RmrkTraitsResourceResourceTypes, RmrkTraitsResourceSlotResource, RmrkTraitsTheme, RmrkTraitsThemeThemeProperty, SpCoreEcdsaSignature, SpCoreEd25519Signature, SpCoreSr25519Signature, SpCoreVoid, SpRuntimeArithmeticError, SpRuntimeDigest, SpRuntimeDigestDigestItem, SpRuntimeDispatchError, SpRuntimeModuleError, SpRuntimeMultiSignature, SpRuntimeTokenError, SpRuntimeTransactionalError, SpTrieStorageProof, SpVersionRuntimeVersion, UpDataStructsAccessMode, UpDataStructsCollection, UpDataStructsCollectionLimits, UpDataStructsCollectionMode, UpDataStructsCollectionPermissions, UpDataStructsCollectionStats, UpDataStructsCreateCollectionData, UpDataStructsCreateFungibleData, UpDataStructsCreateItemData, UpDataStructsCreateItemExData, UpDataStructsCreateNftData, UpDataStructsCreateNftExData, UpDataStructsCreateReFungibleData, UpDataStructsCreateRefungibleExData, UpDataStructsNestingPermissions, UpDataStructsOwnerRestrictedSet, UpDataStructsProperties, UpDataStructsPropertiesMapBoundedVec, UpDataStructsPropertiesMapPropertyPermission, UpDataStructsProperty, UpDataStructsPropertyKeyPermission, UpDataStructsPropertyPermission, UpDataStructsPropertyScope, UpDataStructsRpcCollection, UpDataStructsSponsoringRateLimit, UpDataStructsSponsorshipState, UpDataStructsTokenChild, UpDataStructsTokenData, XcmDoubleEncoded, XcmV0Junction, XcmV0JunctionBodyId, XcmV0JunctionBodyPart, XcmV0JunctionNetworkId, XcmV0MultiAsset, XcmV0MultiLocation, XcmV0Order, XcmV0OriginKind, XcmV0Response, XcmV0Xcm, XcmV1Junction, XcmV1MultiAsset, XcmV1MultiLocation, XcmV1MultiassetAssetId, XcmV1MultiassetAssetInstance, XcmV1MultiassetFungibility, XcmV1MultiassetMultiAssetFilter, XcmV1MultiassetMultiAssets, XcmV1MultiassetWildFungibility, XcmV1MultiassetWildMultiAsset, XcmV1MultilocationJunctions, XcmV1Order, XcmV1Response, XcmV1Xcm, XcmV2Instruction, XcmV2Response, XcmV2TraitsError, XcmV2TraitsOutcome, XcmV2WeightLimit, XcmV2Xcm, XcmVersionedMultiAssets, XcmVersionedMultiLocation, XcmVersionedXcm } from '@polkadot/types/lookup'; declare module '@polkadot/types/types/registry' { export interface InterfaceTypes { @@ -59,7 +59,6 @@ declare module '@polkadot/types/types/registry' { FrameSupportWeightsPerDispatchClassU64: FrameSupportWeightsPerDispatchClassU64; FrameSupportWeightsPerDispatchClassWeightsPerClass: FrameSupportWeightsPerDispatchClassWeightsPerClass; FrameSupportWeightsRuntimeDbWeight: FrameSupportWeightsRuntimeDbWeight; - FrameSupportWeightsWeightToFeeCoefficient: FrameSupportWeightsWeightToFeeCoefficient; FrameSystemAccountInfo: FrameSystemAccountInfo; FrameSystemCall: FrameSystemCall; FrameSystemError: FrameSystemError; @@ -204,6 +203,7 @@ declare module '@polkadot/types/types/registry' { UpDataStructsProperty: UpDataStructsProperty; UpDataStructsPropertyKeyPermission: UpDataStructsPropertyKeyPermission; UpDataStructsPropertyPermission: UpDataStructsPropertyPermission; + UpDataStructsPropertyScope: UpDataStructsPropertyScope; UpDataStructsRpcCollection: UpDataStructsRpcCollection; UpDataStructsSponsoringRateLimit: UpDataStructsSponsoringRateLimit; UpDataStructsSponsorshipState: UpDataStructsSponsorshipState; diff --git a/tests/src/interfaces/types-lookup.ts b/tests/src/interfaces/types-lookup.ts index 1af32273d7..4a299d4fe2 100644 --- a/tests/src/interfaces/types-lookup.ts +++ b/tests/src/interfaces/types-lookup.ts @@ -108,14 +108,23 @@ declare module '@polkadot/types/lookup' { export interface CumulusPalletParachainSystemEvent extends Enum { readonly isValidationFunctionStored: boolean; readonly isValidationFunctionApplied: boolean; - readonly asValidationFunctionApplied: u32; + readonly asValidationFunctionApplied: { + readonly relayChainBlockNum: u32; + } & Struct; readonly isValidationFunctionDiscarded: boolean; readonly isUpgradeAuthorized: boolean; - readonly asUpgradeAuthorized: H256; + readonly asUpgradeAuthorized: { + readonly codeHash: H256; + } & Struct; readonly isDownwardMessagesReceived: boolean; - readonly asDownwardMessagesReceived: u32; + readonly asDownwardMessagesReceived: { + readonly count: u32; + } & Struct; readonly isDownwardMessagesProcessed: boolean; - readonly asDownwardMessagesProcessed: ITuple<[u64, H256]>; + readonly asDownwardMessagesProcessed: { + readonly weightUsed: u64; + readonly dmqHead: H256; + } & Struct; readonly type: 'ValidationFunctionStored' | 'ValidationFunctionApplied' | 'ValidationFunctionDiscarded' | 'UpgradeAuthorized' | 'DownwardMessagesReceived' | 'DownwardMessagesProcessed'; } @@ -300,15 +309,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'V1Ancient' | 'V2'; } - /** @name FrameSupportWeightsWeightToFeeCoefficient (68) */ - export interface FrameSupportWeightsWeightToFeeCoefficient extends Struct { - readonly coeffInteger: u128; - readonly coeffFrac: Perbill; - readonly negative: bool; - readonly degree: u8; - } - - /** @name PalletTreasuryProposal (70) */ + /** @name PalletTreasuryProposal (67) */ export interface PalletTreasuryProposal extends Struct { readonly proposer: AccountId32; readonly value: u128; @@ -316,7 +317,7 @@ declare module '@polkadot/types/lookup' { readonly bond: u128; } - /** @name PalletTreasuryCall (73) */ + /** @name PalletTreasuryCall (70) */ export interface PalletTreasuryCall extends Enum { readonly isProposeSpend: boolean; readonly asProposeSpend: { @@ -338,7 +339,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'ProposeSpend' | 'RejectProposal' | 'ApproveProposal' | 'RemoveApproval'; } - /** @name PalletTreasuryEvent (75) */ + /** @name PalletTreasuryEvent (72) */ export interface PalletTreasuryEvent extends Enum { readonly isProposed: boolean; readonly asProposed: { @@ -374,10 +375,10 @@ declare module '@polkadot/types/lookup' { readonly type: 'Proposed' | 'Spending' | 'Awarded' | 'Rejected' | 'Burnt' | 'Rollover' | 'Deposit'; } - /** @name FrameSupportPalletId (78) */ + /** @name FrameSupportPalletId (75) */ export interface FrameSupportPalletId extends U8aFixed {} - /** @name PalletTreasuryError (79) */ + /** @name PalletTreasuryError (76) */ export interface PalletTreasuryError extends Enum { readonly isInsufficientProposersBalance: boolean; readonly isInvalidIndex: boolean; @@ -386,7 +387,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'InsufficientProposersBalance' | 'InvalidIndex' | 'TooManyApprovals' | 'ProposalNotApproved'; } - /** @name PalletSudoCall (80) */ + /** @name PalletSudoCall (77) */ export interface PalletSudoCall extends Enum { readonly isSudo: boolean; readonly asSudo: { @@ -409,7 +410,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Sudo' | 'SudoUncheckedWeight' | 'SetKey' | 'SudoAs'; } - /** @name FrameSystemCall (82) */ + /** @name FrameSystemCall (79) */ export interface FrameSystemCall extends Enum { readonly isFillBlock: boolean; readonly asFillBlock: { @@ -451,7 +452,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'FillBlock' | 'Remark' | 'SetHeapPages' | 'SetCode' | 'SetCodeWithoutChecks' | 'SetStorage' | 'KillStorage' | 'KillPrefix' | 'RemarkWithEvent'; } - /** @name OrmlVestingModuleCall (85) */ + /** @name OrmlVestingModuleCall (83) */ export interface OrmlVestingModuleCall extends Enum { readonly isClaim: boolean; readonly isVestedTransfer: boolean; @@ -471,7 +472,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Claim' | 'VestedTransfer' | 'UpdateVestingSchedules' | 'ClaimFor'; } - /** @name OrmlVestingVestingSchedule (86) */ + /** @name OrmlVestingVestingSchedule (84) */ export interface OrmlVestingVestingSchedule extends Struct { readonly start: u32; readonly period: u32; @@ -479,7 +480,7 @@ declare module '@polkadot/types/lookup' { readonly perPeriod: Compact; } - /** @name CumulusPalletXcmpQueueCall (88) */ + /** @name CumulusPalletXcmpQueueCall (86) */ export interface CumulusPalletXcmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -515,7 +516,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'ServiceOverweight' | 'SuspendXcmExecution' | 'ResumeXcmExecution' | 'UpdateSuspendThreshold' | 'UpdateDropThreshold' | 'UpdateResumeThreshold' | 'UpdateThresholdWeight' | 'UpdateWeightRestrictDecay' | 'UpdateXcmpMaxIndividualWeight'; } - /** @name PalletXcmCall (89) */ + /** @name PalletXcmCall (87) */ export interface PalletXcmCall extends Enum { readonly isSend: boolean; readonly asSend: { @@ -577,7 +578,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Send' | 'TeleportAssets' | 'ReserveTransferAssets' | 'Execute' | 'ForceXcmVersion' | 'ForceDefaultXcmVersion' | 'ForceSubscribeVersionNotify' | 'ForceUnsubscribeVersionNotify' | 'LimitedReserveTransferAssets' | 'LimitedTeleportAssets'; } - /** @name XcmVersionedMultiLocation (90) */ + /** @name XcmVersionedMultiLocation (88) */ export interface XcmVersionedMultiLocation extends Enum { readonly isV0: boolean; readonly asV0: XcmV0MultiLocation; @@ -586,7 +587,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'V0' | 'V1'; } - /** @name XcmV0MultiLocation (91) */ + /** @name XcmV0MultiLocation (89) */ export interface XcmV0MultiLocation extends Enum { readonly isNull: boolean; readonly isX1: boolean; @@ -608,7 +609,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Null' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; } - /** @name XcmV0Junction (92) */ + /** @name XcmV0Junction (90) */ export interface XcmV0Junction extends Enum { readonly isParent: boolean; readonly isParachain: boolean; @@ -643,7 +644,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Parent' | 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; } - /** @name XcmV0JunctionNetworkId (93) */ + /** @name XcmV0JunctionNetworkId (91) */ export interface XcmV0JunctionNetworkId extends Enum { readonly isAny: boolean; readonly isNamed: boolean; @@ -653,7 +654,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Any' | 'Named' | 'Polkadot' | 'Kusama'; } - /** @name XcmV0JunctionBodyId (94) */ + /** @name XcmV0JunctionBodyId (92) */ export interface XcmV0JunctionBodyId extends Enum { readonly isUnit: boolean; readonly isNamed: boolean; @@ -667,7 +668,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Unit' | 'Named' | 'Index' | 'Executive' | 'Technical' | 'Legislative' | 'Judicial'; } - /** @name XcmV0JunctionBodyPart (95) */ + /** @name XcmV0JunctionBodyPart (93) */ export interface XcmV0JunctionBodyPart extends Enum { readonly isVoice: boolean; readonly isMembers: boolean; @@ -692,13 +693,13 @@ declare module '@polkadot/types/lookup' { readonly type: 'Voice' | 'Members' | 'Fraction' | 'AtLeastProportion' | 'MoreThanProportion'; } - /** @name XcmV1MultiLocation (96) */ + /** @name XcmV1MultiLocation (94) */ export interface XcmV1MultiLocation extends Struct { readonly parents: u8; readonly interior: XcmV1MultilocationJunctions; } - /** @name XcmV1MultilocationJunctions (97) */ + /** @name XcmV1MultilocationJunctions (95) */ export interface XcmV1MultilocationJunctions extends Enum { readonly isHere: boolean; readonly isX1: boolean; @@ -720,7 +721,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Here' | 'X1' | 'X2' | 'X3' | 'X4' | 'X5' | 'X6' | 'X7' | 'X8'; } - /** @name XcmV1Junction (98) */ + /** @name XcmV1Junction (96) */ export interface XcmV1Junction extends Enum { readonly isParachain: boolean; readonly asParachain: Compact; @@ -754,7 +755,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Parachain' | 'AccountId32' | 'AccountIndex64' | 'AccountKey20' | 'PalletInstance' | 'GeneralIndex' | 'GeneralKey' | 'OnlyChild' | 'Plurality'; } - /** @name XcmVersionedXcm (99) */ + /** @name XcmVersionedXcm (97) */ export interface XcmVersionedXcm extends Enum { readonly isV0: boolean; readonly asV0: XcmV0Xcm; @@ -765,7 +766,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'V0' | 'V1' | 'V2'; } - /** @name XcmV0Xcm (100) */ + /** @name XcmV0Xcm (98) */ export interface XcmV0Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -828,7 +829,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'WithdrawAsset' | 'ReserveAssetDeposit' | 'TeleportAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom'; } - /** @name XcmV0MultiAsset (102) */ + /** @name XcmV0MultiAsset (100) */ export interface XcmV0MultiAsset extends Enum { readonly isNone: boolean; readonly isAll: boolean; @@ -873,7 +874,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'None' | 'All' | 'AllFungible' | 'AllNonFungible' | 'AllAbstractFungible' | 'AllAbstractNonFungible' | 'AllConcreteFungible' | 'AllConcreteNonFungible' | 'AbstractFungible' | 'AbstractNonFungible' | 'ConcreteFungible' | 'ConcreteNonFungible'; } - /** @name XcmV1MultiassetAssetInstance (103) */ + /** @name XcmV1MultiassetAssetInstance (101) */ export interface XcmV1MultiassetAssetInstance extends Enum { readonly isUndefined: boolean; readonly isIndex: boolean; @@ -891,7 +892,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Undefined' | 'Index' | 'Array4' | 'Array8' | 'Array16' | 'Array32' | 'Blob'; } - /** @name XcmV0Order (106) */ + /** @name XcmV0Order (104) */ export interface XcmV0Order extends Enum { readonly isNull: boolean; readonly isDepositAsset: boolean; @@ -939,14 +940,14 @@ declare module '@polkadot/types/lookup' { readonly type: 'Null' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV0Response (108) */ + /** @name XcmV0Response (106) */ export interface XcmV0Response extends Enum { readonly isAssets: boolean; readonly asAssets: Vec; readonly type: 'Assets'; } - /** @name XcmV0OriginKind (109) */ + /** @name XcmV0OriginKind (107) */ export interface XcmV0OriginKind extends Enum { readonly isNative: boolean; readonly isSovereignAccount: boolean; @@ -955,12 +956,12 @@ declare module '@polkadot/types/lookup' { readonly type: 'Native' | 'SovereignAccount' | 'Superuser' | 'Xcm'; } - /** @name XcmDoubleEncoded (110) */ + /** @name XcmDoubleEncoded (108) */ export interface XcmDoubleEncoded extends Struct { readonly encoded: Bytes; } - /** @name XcmV1Xcm (111) */ + /** @name XcmV1Xcm (109) */ export interface XcmV1Xcm extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: { @@ -1029,16 +1030,16 @@ declare module '@polkadot/types/lookup' { readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'RelayedFrom' | 'SubscribeVersion' | 'UnsubscribeVersion'; } - /** @name XcmV1MultiassetMultiAssets (112) */ + /** @name XcmV1MultiassetMultiAssets (110) */ export interface XcmV1MultiassetMultiAssets extends Vec {} - /** @name XcmV1MultiAsset (114) */ + /** @name XcmV1MultiAsset (112) */ export interface XcmV1MultiAsset extends Struct { readonly id: XcmV1MultiassetAssetId; readonly fun: XcmV1MultiassetFungibility; } - /** @name XcmV1MultiassetAssetId (115) */ + /** @name XcmV1MultiassetAssetId (113) */ export interface XcmV1MultiassetAssetId extends Enum { readonly isConcrete: boolean; readonly asConcrete: XcmV1MultiLocation; @@ -1047,7 +1048,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Concrete' | 'Abstract'; } - /** @name XcmV1MultiassetFungibility (116) */ + /** @name XcmV1MultiassetFungibility (114) */ export interface XcmV1MultiassetFungibility extends Enum { readonly isFungible: boolean; readonly asFungible: Compact; @@ -1056,7 +1057,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Fungible' | 'NonFungible'; } - /** @name XcmV1Order (118) */ + /** @name XcmV1Order (116) */ export interface XcmV1Order extends Enum { readonly isNoop: boolean; readonly isDepositAsset: boolean; @@ -1106,7 +1107,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Noop' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution'; } - /** @name XcmV1MultiassetMultiAssetFilter (119) */ + /** @name XcmV1MultiassetMultiAssetFilter (117) */ export interface XcmV1MultiassetMultiAssetFilter extends Enum { readonly isDefinite: boolean; readonly asDefinite: XcmV1MultiassetMultiAssets; @@ -1115,7 +1116,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Definite' | 'Wild'; } - /** @name XcmV1MultiassetWildMultiAsset (120) */ + /** @name XcmV1MultiassetWildMultiAsset (118) */ export interface XcmV1MultiassetWildMultiAsset extends Enum { readonly isAll: boolean; readonly isAllOf: boolean; @@ -1126,14 +1127,14 @@ declare module '@polkadot/types/lookup' { readonly type: 'All' | 'AllOf'; } - /** @name XcmV1MultiassetWildFungibility (121) */ + /** @name XcmV1MultiassetWildFungibility (119) */ export interface XcmV1MultiassetWildFungibility extends Enum { readonly isFungible: boolean; readonly isNonFungible: boolean; readonly type: 'Fungible' | 'NonFungible'; } - /** @name XcmV1Response (123) */ + /** @name XcmV1Response (121) */ export interface XcmV1Response extends Enum { readonly isAssets: boolean; readonly asAssets: XcmV1MultiassetMultiAssets; @@ -1142,10 +1143,10 @@ declare module '@polkadot/types/lookup' { readonly type: 'Assets' | 'Version'; } - /** @name XcmV2Xcm (124) */ + /** @name XcmV2Xcm (122) */ export interface XcmV2Xcm extends Vec {} - /** @name XcmV2Instruction (126) */ + /** @name XcmV2Instruction (124) */ export interface XcmV2Instruction extends Enum { readonly isWithdrawAsset: boolean; readonly asWithdrawAsset: XcmV1MultiassetMultiAssets; @@ -1265,7 +1266,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'WithdrawAsset' | 'ReserveAssetDeposited' | 'ReceiveTeleportedAsset' | 'QueryResponse' | 'TransferAsset' | 'TransferReserveAsset' | 'Transact' | 'HrmpNewChannelOpenRequest' | 'HrmpChannelAccepted' | 'HrmpChannelClosing' | 'ClearOrigin' | 'DescendOrigin' | 'ReportError' | 'DepositAsset' | 'DepositReserveAsset' | 'ExchangeAsset' | 'InitiateReserveWithdraw' | 'InitiateTeleport' | 'QueryHolding' | 'BuyExecution' | 'RefundSurplus' | 'SetErrorHandler' | 'SetAppendix' | 'ClearError' | 'ClaimAsset' | 'Trap' | 'SubscribeVersion' | 'UnsubscribeVersion'; } - /** @name XcmV2Response (127) */ + /** @name XcmV2Response (125) */ export interface XcmV2Response extends Enum { readonly isNull: boolean; readonly isAssets: boolean; @@ -1277,7 +1278,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Null' | 'Assets' | 'ExecutionResult' | 'Version'; } - /** @name XcmV2TraitsError (130) */ + /** @name XcmV2TraitsError (128) */ export interface XcmV2TraitsError extends Enum { readonly isOverflow: boolean; readonly isUnimplemented: boolean; @@ -1310,7 +1311,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Overflow' | 'Unimplemented' | 'UntrustedReserveLocation' | 'UntrustedTeleportLocation' | 'MultiLocationFull' | 'MultiLocationNotInvertible' | 'BadOrigin' | 'InvalidLocation' | 'AssetNotFound' | 'FailedToTransactAsset' | 'NotWithdrawable' | 'LocationCannotHold' | 'ExceedsMaxMessageSize' | 'DestinationUnsupported' | 'Transport' | 'Unroutable' | 'UnknownClaim' | 'FailedToDecode' | 'MaxWeightInvalid' | 'NotHoldingFees' | 'TooExpensive' | 'Trap' | 'UnhandledXcmVersion' | 'WeightLimitReached' | 'Barrier' | 'WeightNotComputable'; } - /** @name XcmV2WeightLimit (131) */ + /** @name XcmV2WeightLimit (129) */ export interface XcmV2WeightLimit extends Enum { readonly isUnlimited: boolean; readonly isLimited: boolean; @@ -1318,7 +1319,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Unlimited' | 'Limited'; } - /** @name XcmVersionedMultiAssets (132) */ + /** @name XcmVersionedMultiAssets (130) */ export interface XcmVersionedMultiAssets extends Enum { readonly isV0: boolean; readonly asV0: Vec; @@ -1327,10 +1328,10 @@ declare module '@polkadot/types/lookup' { readonly type: 'V0' | 'V1'; } - /** @name CumulusPalletXcmCall (147) */ + /** @name CumulusPalletXcmCall (145) */ export type CumulusPalletXcmCall = Null; - /** @name CumulusPalletDmpQueueCall (148) */ + /** @name CumulusPalletDmpQueueCall (146) */ export interface CumulusPalletDmpQueueCall extends Enum { readonly isServiceOverweight: boolean; readonly asServiceOverweight: { @@ -1340,7 +1341,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'ServiceOverweight'; } - /** @name PalletInflationCall (149) */ + /** @name PalletInflationCall (147) */ export interface PalletInflationCall extends Enum { readonly isStartInflation: boolean; readonly asStartInflation: { @@ -1349,7 +1350,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'StartInflation'; } - /** @name PalletUniqueCall (150) */ + /** @name PalletUniqueCall (148) */ export interface PalletUniqueCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -1498,10 +1499,16 @@ declare module '@polkadot/types/lookup' { readonly collectionId: u32; readonly newLimit: UpDataStructsCollectionPermissions; } & Struct; - readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions'; + readonly isRepartition: boolean; + readonly asRepartition: { + readonly collectionId: u32; + readonly token: u32; + readonly amount: u128; + } & Struct; + readonly type: 'CreateCollection' | 'CreateCollectionEx' | 'DestroyCollection' | 'AddToAllowList' | 'RemoveFromAllowList' | 'ChangeCollectionOwner' | 'AddCollectionAdmin' | 'RemoveCollectionAdmin' | 'SetCollectionSponsor' | 'ConfirmSponsorship' | 'RemoveCollectionSponsor' | 'CreateItem' | 'CreateMultipleItems' | 'SetCollectionProperties' | 'DeleteCollectionProperties' | 'SetTokenProperties' | 'DeleteTokenProperties' | 'SetTokenPropertyPermissions' | 'CreateMultipleItemsEx' | 'SetTransfersEnabledFlag' | 'BurnItem' | 'BurnFrom' | 'Transfer' | 'Approve' | 'TransferFrom' | 'SetCollectionLimits' | 'SetCollectionPermissions' | 'Repartition'; } - /** @name UpDataStructsCollectionMode (156) */ + /** @name UpDataStructsCollectionMode (154) */ export interface UpDataStructsCollectionMode extends Enum { readonly isNft: boolean; readonly isFungible: boolean; @@ -1510,7 +1517,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateCollectionData (157) */ + /** @name UpDataStructsCreateCollectionData (155) */ export interface UpDataStructsCreateCollectionData extends Struct { readonly mode: UpDataStructsCollectionMode; readonly access: Option; @@ -1524,14 +1531,14 @@ declare module '@polkadot/types/lookup' { readonly properties: Vec; } - /** @name UpDataStructsAccessMode (159) */ + /** @name UpDataStructsAccessMode (157) */ export interface UpDataStructsAccessMode extends Enum { readonly isNormal: boolean; readonly isAllowList: boolean; readonly type: 'Normal' | 'AllowList'; } - /** @name UpDataStructsCollectionLimits (162) */ + /** @name UpDataStructsCollectionLimits (160) */ export interface UpDataStructsCollectionLimits extends Struct { readonly accountTokenOwnershipLimit: Option; readonly sponsoredDataSize: Option; @@ -1544,7 +1551,7 @@ declare module '@polkadot/types/lookup' { readonly transfersEnabled: Option; } - /** @name UpDataStructsSponsoringRateLimit (164) */ + /** @name UpDataStructsSponsoringRateLimit (162) */ export interface UpDataStructsSponsoringRateLimit extends Enum { readonly isSponsoringDisabled: boolean; readonly isBlocks: boolean; @@ -1552,44 +1559,43 @@ declare module '@polkadot/types/lookup' { readonly type: 'SponsoringDisabled' | 'Blocks'; } - /** @name UpDataStructsCollectionPermissions (167) */ + /** @name UpDataStructsCollectionPermissions (165) */ export interface UpDataStructsCollectionPermissions extends Struct { readonly access: Option; readonly mintMode: Option; readonly nesting: Option; } - /** @name UpDataStructsNestingPermissions (169) */ + /** @name UpDataStructsNestingPermissions (167) */ export interface UpDataStructsNestingPermissions extends Struct { readonly tokenOwner: bool; readonly collectionAdmin: bool; readonly restricted: Option; - readonly permissive: bool; } - /** @name UpDataStructsOwnerRestrictedSet (171) */ + /** @name UpDataStructsOwnerRestrictedSet (169) */ export interface UpDataStructsOwnerRestrictedSet extends BTreeSet {} - /** @name UpDataStructsPropertyKeyPermission (177) */ + /** @name UpDataStructsPropertyKeyPermission (175) */ export interface UpDataStructsPropertyKeyPermission extends Struct { readonly key: Bytes; readonly permission: UpDataStructsPropertyPermission; } - /** @name UpDataStructsPropertyPermission (179) */ + /** @name UpDataStructsPropertyPermission (177) */ export interface UpDataStructsPropertyPermission extends Struct { readonly mutable: bool; readonly collectionAdmin: bool; readonly tokenOwner: bool; } - /** @name UpDataStructsProperty (182) */ + /** @name UpDataStructsProperty (180) */ export interface UpDataStructsProperty extends Struct { readonly key: Bytes; readonly value: Bytes; } - /** @name PalletEvmAccountBasicCrossAccountIdRepr (185) */ + /** @name PalletEvmAccountBasicCrossAccountIdRepr (183) */ export interface PalletEvmAccountBasicCrossAccountIdRepr extends Enum { readonly isSubstrate: boolean; readonly asSubstrate: AccountId32; @@ -1598,7 +1604,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Substrate' | 'Ethereum'; } - /** @name UpDataStructsCreateItemData (187) */ + /** @name UpDataStructsCreateItemData (185) */ export interface UpDataStructsCreateItemData extends Enum { readonly isNft: boolean; readonly asNft: UpDataStructsCreateNftData; @@ -1609,23 +1615,23 @@ declare module '@polkadot/types/lookup' { readonly type: 'Nft' | 'Fungible' | 'ReFungible'; } - /** @name UpDataStructsCreateNftData (188) */ + /** @name UpDataStructsCreateNftData (186) */ export interface UpDataStructsCreateNftData extends Struct { readonly properties: Vec; } - /** @name UpDataStructsCreateFungibleData (189) */ + /** @name UpDataStructsCreateFungibleData (187) */ export interface UpDataStructsCreateFungibleData extends Struct { readonly value: u128; } - /** @name UpDataStructsCreateReFungibleData (190) */ + /** @name UpDataStructsCreateReFungibleData (188) */ export interface UpDataStructsCreateReFungibleData extends Struct { readonly constData: Bytes; readonly pieces: u128; } - /** @name UpDataStructsCreateItemExData (195) */ + /** @name UpDataStructsCreateItemExData (193) */ export interface UpDataStructsCreateItemExData extends Enum { readonly isNft: boolean; readonly asNft: Vec; @@ -1638,19 +1644,19 @@ declare module '@polkadot/types/lookup' { readonly type: 'Nft' | 'Fungible' | 'RefungibleMultipleItems' | 'RefungibleMultipleOwners'; } - /** @name UpDataStructsCreateNftExData (197) */ + /** @name UpDataStructsCreateNftExData (195) */ export interface UpDataStructsCreateNftExData extends Struct { readonly properties: Vec; readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name UpDataStructsCreateRefungibleExData (204) */ + /** @name UpDataStructsCreateRefungibleExData (202) */ export interface UpDataStructsCreateRefungibleExData extends Struct { readonly constData: Bytes; readonly users: BTreeMap; } - /** @name PalletUniqueSchedulerCall (206) */ + /** @name PalletUniqueSchedulerCall (204) */ export interface PalletUniqueSchedulerCall extends Enum { readonly isScheduleNamed: boolean; readonly asScheduleNamed: { @@ -1675,7 +1681,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'ScheduleNamed' | 'CancelNamed' | 'ScheduleNamedAfter'; } - /** @name FrameSupportScheduleMaybeHashed (208) */ + /** @name FrameSupportScheduleMaybeHashed (206) */ export interface FrameSupportScheduleMaybeHashed extends Enum { readonly isValue: boolean; readonly asValue: Call; @@ -1684,13 +1690,13 @@ declare module '@polkadot/types/lookup' { readonly type: 'Value' | 'Hash'; } - /** @name PalletTemplateTransactionPaymentCall (209) */ + /** @name PalletTemplateTransactionPaymentCall (207) */ export type PalletTemplateTransactionPaymentCall = Null; - /** @name PalletStructureCall (210) */ + /** @name PalletStructureCall (208) */ export type PalletStructureCall = Null; - /** @name PalletRmrkCoreCall (211) */ + /** @name PalletRmrkCoreCall (209) */ export interface PalletRmrkCoreCall extends Enum { readonly isCreateCollection: boolean; readonly asCreateCollection: { @@ -1713,7 +1719,7 @@ declare module '@polkadot/types/lookup' { } & Struct; readonly isMintNft: boolean; readonly asMintNft: { - readonly owner: AccountId32; + readonly owner: Option; readonly collectionId: u32; readonly recipient: Option; readonly royaltyAmount: Option; @@ -1748,13 +1754,13 @@ declare module '@polkadot/types/lookup' { readonly asAcceptResource: { readonly rmrkCollectionId: u32; readonly rmrkNftId: u32; - readonly rmrkResourceId: u32; + readonly resourceId: u32; } & Struct; readonly isAcceptResourceRemoval: boolean; readonly asAcceptResourceRemoval: { readonly rmrkCollectionId: u32; readonly rmrkNftId: u32; - readonly rmrkResourceId: u32; + readonly resourceId: u32; } & Struct; readonly isSetProperty: boolean; readonly asSetProperty: { @@ -1796,7 +1802,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'CreateCollection' | 'DestroyCollection' | 'ChangeCollectionIssuer' | 'LockCollection' | 'MintNft' | 'BurnNft' | 'Send' | 'AcceptNft' | 'RejectNft' | 'AcceptResource' | 'AcceptResourceRemoval' | 'SetProperty' | 'SetPriority' | 'AddBasicResource' | 'AddComposableResource' | 'AddSlotResource' | 'RemoveResource'; } - /** @name RmrkTraitsResourceResourceTypes (217) */ + /** @name RmrkTraitsResourceResourceTypes (215) */ export interface RmrkTraitsResourceResourceTypes extends Enum { readonly isBasic: boolean; readonly asBasic: RmrkTraitsResourceBasicResource; @@ -1807,7 +1813,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Basic' | 'Composable' | 'Slot'; } - /** @name RmrkTraitsResourceBasicResource (219) */ + /** @name RmrkTraitsResourceBasicResource (217) */ export interface RmrkTraitsResourceBasicResource extends Struct { readonly src: Option; readonly metadata: Option; @@ -1815,7 +1821,7 @@ declare module '@polkadot/types/lookup' { readonly thumb: Option; } - /** @name RmrkTraitsResourceComposableResource (221) */ + /** @name RmrkTraitsResourceComposableResource (219) */ export interface RmrkTraitsResourceComposableResource extends Struct { readonly parts: Vec; readonly base: u32; @@ -1825,7 +1831,7 @@ declare module '@polkadot/types/lookup' { readonly thumb: Option; } - /** @name RmrkTraitsResourceSlotResource (222) */ + /** @name RmrkTraitsResourceSlotResource (220) */ export interface RmrkTraitsResourceSlotResource extends Struct { readonly base: u32; readonly src: Option; @@ -1835,7 +1841,7 @@ declare module '@polkadot/types/lookup' { readonly thumb: Option; } - /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (224) */ + /** @name RmrkTraitsNftAccountIdOrCollectionNftTuple (222) */ export interface RmrkTraitsNftAccountIdOrCollectionNftTuple extends Enum { readonly isAccountId: boolean; readonly asAccountId: AccountId32; @@ -1844,7 +1850,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'AccountId' | 'CollectionAndNftTuple'; } - /** @name PalletRmrkEquipCall (228) */ + /** @name PalletRmrkEquipCall (226) */ export interface PalletRmrkEquipCall extends Enum { readonly isCreateBase: boolean; readonly asCreateBase: { @@ -1857,10 +1863,16 @@ declare module '@polkadot/types/lookup' { readonly baseId: u32; readonly theme: RmrkTraitsTheme; } & Struct; - readonly type: 'CreateBase' | 'ThemeAdd'; + readonly isEquippable: boolean; + readonly asEquippable: { + readonly baseId: u32; + readonly slotId: u32; + readonly equippables: RmrkTraitsPartEquippableList; + } & Struct; + readonly type: 'CreateBase' | 'ThemeAdd' | 'Equippable'; } - /** @name RmrkTraitsPartPartType (230) */ + /** @name RmrkTraitsPartPartType (229) */ export interface RmrkTraitsPartPartType extends Enum { readonly isFixedPart: boolean; readonly asFixedPart: RmrkTraitsPartFixedPart; @@ -1869,14 +1881,14 @@ declare module '@polkadot/types/lookup' { readonly type: 'FixedPart' | 'SlotPart'; } - /** @name RmrkTraitsPartFixedPart (232) */ + /** @name RmrkTraitsPartFixedPart (231) */ export interface RmrkTraitsPartFixedPart extends Struct { readonly id: u32; readonly z: u32; readonly src: Bytes; } - /** @name RmrkTraitsPartSlotPart (233) */ + /** @name RmrkTraitsPartSlotPart (232) */ export interface RmrkTraitsPartSlotPart extends Struct { readonly id: u32; readonly equippable: RmrkTraitsPartEquippableList; @@ -1884,7 +1896,7 @@ declare module '@polkadot/types/lookup' { readonly z: u32; } - /** @name RmrkTraitsPartEquippableList (234) */ + /** @name RmrkTraitsPartEquippableList (233) */ export interface RmrkTraitsPartEquippableList extends Enum { readonly isAll: boolean; readonly isEmpty: boolean; @@ -1893,14 +1905,14 @@ declare module '@polkadot/types/lookup' { readonly type: 'All' | 'Empty' | 'Custom'; } - /** @name RmrkTraitsTheme (236) */ + /** @name RmrkTraitsTheme (235) */ export interface RmrkTraitsTheme extends Struct { readonly name: Bytes; readonly properties: Vec; readonly inherit: bool; } - /** @name RmrkTraitsThemeThemeProperty (238) */ + /** @name RmrkTraitsThemeThemeProperty (237) */ export interface RmrkTraitsThemeThemeProperty extends Struct { readonly key: Bytes; readonly value: Bytes; @@ -2323,17 +2335,35 @@ declare module '@polkadot/types/lookup' { /** @name CumulusPalletDmpQueueEvent (284) */ export interface CumulusPalletDmpQueueEvent extends Enum { readonly isInvalidFormat: boolean; - readonly asInvalidFormat: U8aFixed; + readonly asInvalidFormat: { + readonly messageId: U8aFixed; + } & Struct; readonly isUnsupportedVersion: boolean; - readonly asUnsupportedVersion: U8aFixed; + readonly asUnsupportedVersion: { + readonly messageId: U8aFixed; + } & Struct; readonly isExecutedDownward: boolean; - readonly asExecutedDownward: ITuple<[U8aFixed, XcmV2TraitsOutcome]>; + readonly asExecutedDownward: { + readonly messageId: U8aFixed; + readonly outcome: XcmV2TraitsOutcome; + } & Struct; readonly isWeightExhausted: boolean; - readonly asWeightExhausted: ITuple<[U8aFixed, u64, u64]>; + readonly asWeightExhausted: { + readonly messageId: U8aFixed; + readonly remainingWeight: u64; + readonly requiredWeight: u64; + } & Struct; readonly isOverweightEnqueued: boolean; - readonly asOverweightEnqueued: ITuple<[U8aFixed, u64, u64]>; + readonly asOverweightEnqueued: { + readonly messageId: U8aFixed; + readonly overweightIndex: u64; + readonly requiredWeight: u64; + } & Struct; readonly isOverweightServiced: boolean; - readonly asOverweightServiced: ITuple<[u64, u64]>; + readonly asOverweightServiced: { + readonly overweightIndex: u64; + readonly weightUsed: u64; + } & Struct; readonly type: 'InvalidFormat' | 'UnsupportedVersion' | 'ExecutedDownward' | 'WeightExhausted' | 'OverweightEnqueued' | 'OverweightServiced'; } @@ -2527,7 +2557,12 @@ declare module '@polkadot/types/lookup' { readonly issuer: AccountId32; readonly baseId: u32; } & Struct; - readonly type: 'BaseCreated'; + readonly isEquippablesUpdated: boolean; + readonly asEquippablesUpdated: { + readonly baseId: u32; + readonly slotId: u32; + } & Struct; + readonly type: 'BaseCreated' | 'EquippablesUpdated'; } /** @name PalletEvmEvent (293) */ @@ -2814,7 +2849,8 @@ declare module '@polkadot/types/lookup' { readonly isCollectionDecimalPointLimitExceeded: boolean; readonly isConfirmUnsetSponsorFail: boolean; readonly isEmptyArgument: boolean; - readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument'; + readonly isRepartitionCalledOnNonRefungibleCollection: boolean; + readonly type: 'CollectionDecimalPointLimitExceeded' | 'ConfirmUnsetSponsorFail' | 'EmptyArgument' | 'RepartitionCalledOnNonRefungibleCollection'; } /** @name PalletUniqueSchedulerScheduledV3 (347) */ @@ -3067,9 +3103,10 @@ declare module '@polkadot/types/lookup' { export interface PalletRefungibleError extends Enum { readonly isNotRefungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isWrongRefungiblePieces: boolean; + readonly isRepartitionWhileNotOwningAllPieces: boolean; readonly isRefungibleDisallowsNesting: boolean; readonly isSettingPropertiesNotAllowed: boolean; - readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; + readonly type: 'NotRefungibleDataUsedToMintFungibleCollectionToken' | 'WrongRefungiblePieces' | 'RepartitionWhileNotOwningAllPieces' | 'RefungibleDisallowsNesting' | 'SettingPropertiesNotAllowed'; } /** @name PalletNonfungibleItemData (394) */ @@ -3077,7 +3114,14 @@ declare module '@polkadot/types/lookup' { readonly owner: PalletEvmAccountBasicCrossAccountIdRepr; } - /** @name PalletNonfungibleError (396) */ + /** @name UpDataStructsPropertyScope (396) */ + export interface UpDataStructsPropertyScope extends Enum { + readonly isNone: boolean; + readonly isRmrk: boolean; + readonly type: 'None' | 'Rmrk'; + } + + /** @name PalletNonfungibleError (398) */ export interface PalletNonfungibleError extends Enum { readonly isNotNonfungibleDataUsedToMintFungibleCollectionToken: boolean; readonly isNonfungibleItemsHaveNoAmount: boolean; @@ -3085,7 +3129,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'NotNonfungibleDataUsedToMintFungibleCollectionToken' | 'NonfungibleItemsHaveNoAmount' | 'CantBurnNftWithChildren'; } - /** @name PalletStructureError (397) */ + /** @name PalletStructureError (399) */ export interface PalletStructureError extends Enum { readonly isOuroborosDetected: boolean; readonly isDepthLimit: boolean; @@ -3094,12 +3138,13 @@ declare module '@polkadot/types/lookup' { readonly type: 'OuroborosDetected' | 'DepthLimit' | 'BreadthLimit' | 'TokenNotFound'; } - /** @name PalletRmrkCoreError (398) */ + /** @name PalletRmrkCoreError (400) */ export interface PalletRmrkCoreError extends Enum { readonly isCorruptedCollectionType: boolean; readonly isNftTypeEncodeError: boolean; readonly isRmrkPropertyKeyIsTooLong: boolean; readonly isRmrkPropertyValueIsTooLong: boolean; + readonly isRmrkPropertyIsNotFound: boolean; readonly isUnableToDecodeRmrkData: boolean; readonly isCollectionNotEmpty: boolean; readonly isNoAvailableCollectionId: boolean; @@ -3112,21 +3157,25 @@ declare module '@polkadot/types/lookup' { readonly isCannotSendToDescendentOrSelf: boolean; readonly isCannotAcceptNonOwnedNft: boolean; readonly isCannotRejectNonOwnedNft: boolean; + readonly isCannotRejectNonPendingNft: boolean; readonly isResourceNotPending: boolean; - readonly type: 'CorruptedCollectionType' | 'NftTypeEncodeError' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'ResourceNotPending'; + readonly isNoAvailableResourceId: boolean; + readonly type: 'CorruptedCollectionType' | 'NftTypeEncodeError' | 'RmrkPropertyKeyIsTooLong' | 'RmrkPropertyValueIsTooLong' | 'RmrkPropertyIsNotFound' | 'UnableToDecodeRmrkData' | 'CollectionNotEmpty' | 'NoAvailableCollectionId' | 'NoAvailableNftId' | 'CollectionUnknown' | 'NoPermission' | 'NonTransferable' | 'CollectionFullOrLocked' | 'ResourceDoesntExist' | 'CannotSendToDescendentOrSelf' | 'CannotAcceptNonOwnedNft' | 'CannotRejectNonOwnedNft' | 'CannotRejectNonPendingNft' | 'ResourceNotPending' | 'NoAvailableResourceId'; } - /** @name PalletRmrkEquipError (400) */ + /** @name PalletRmrkEquipError (402) */ export interface PalletRmrkEquipError extends Enum { readonly isPermissionError: boolean; readonly isNoAvailableBaseId: boolean; readonly isNoAvailablePartId: boolean; readonly isBaseDoesntExist: boolean; readonly isNeedsDefaultThemeFirst: boolean; - readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst'; + readonly isPartDoesntExist: boolean; + readonly isNoEquippableOnFixedPart: boolean; + readonly type: 'PermissionError' | 'NoAvailableBaseId' | 'NoAvailablePartId' | 'BaseDoesntExist' | 'NeedsDefaultThemeFirst' | 'PartDoesntExist' | 'NoEquippableOnFixedPart'; } - /** @name PalletEvmError (403) */ + /** @name PalletEvmError (405) */ export interface PalletEvmError extends Enum { readonly isBalanceLow: boolean; readonly isFeeOverflow: boolean; @@ -3137,7 +3186,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'BalanceLow' | 'FeeOverflow' | 'PaymentOverflow' | 'WithdrawFailed' | 'GasPriceTooLow' | 'InvalidNonce'; } - /** @name FpRpcTransactionStatus (406) */ + /** @name FpRpcTransactionStatus (408) */ export interface FpRpcTransactionStatus extends Struct { readonly transactionHash: H256; readonly transactionIndex: u32; @@ -3148,10 +3197,10 @@ declare module '@polkadot/types/lookup' { readonly logsBloom: EthbloomBloom; } - /** @name EthbloomBloom (408) */ + /** @name EthbloomBloom (410) */ export interface EthbloomBloom extends U8aFixed {} - /** @name EthereumReceiptReceiptV3 (410) */ + /** @name EthereumReceiptReceiptV3 (412) */ export interface EthereumReceiptReceiptV3 extends Enum { readonly isLegacy: boolean; readonly asLegacy: EthereumReceiptEip658ReceiptData; @@ -3162,7 +3211,7 @@ declare module '@polkadot/types/lookup' { readonly type: 'Legacy' | 'Eip2930' | 'Eip1559'; } - /** @name EthereumReceiptEip658ReceiptData (411) */ + /** @name EthereumReceiptEip658ReceiptData (413) */ export interface EthereumReceiptEip658ReceiptData extends Struct { readonly statusCode: u8; readonly usedGas: U256; @@ -3170,14 +3219,14 @@ declare module '@polkadot/types/lookup' { readonly logs: Vec; } - /** @name EthereumBlock (412) */ + /** @name EthereumBlock (414) */ export interface EthereumBlock extends Struct { readonly header: EthereumHeader; readonly transactions: Vec; readonly ommers: Vec; } - /** @name EthereumHeader (413) */ + /** @name EthereumHeader (415) */ export interface EthereumHeader extends Struct { readonly parentHash: H256; readonly ommersHash: H256; @@ -3196,24 +3245,24 @@ declare module '@polkadot/types/lookup' { readonly nonce: EthereumTypesHashH64; } - /** @name EthereumTypesHashH64 (414) */ + /** @name EthereumTypesHashH64 (416) */ export interface EthereumTypesHashH64 extends U8aFixed {} - /** @name PalletEthereumError (419) */ + /** @name PalletEthereumError (421) */ export interface PalletEthereumError extends Enum { readonly isInvalidSignature: boolean; readonly isPreLogExists: boolean; readonly type: 'InvalidSignature' | 'PreLogExists'; } - /** @name PalletEvmCoderSubstrateError (420) */ + /** @name PalletEvmCoderSubstrateError (422) */ export interface PalletEvmCoderSubstrateError extends Enum { readonly isOutOfGas: boolean; readonly isOutOfFund: boolean; readonly type: 'OutOfGas' | 'OutOfFund'; } - /** @name PalletEvmContractHelpersSponsoringModeT (421) */ + /** @name PalletEvmContractHelpersSponsoringModeT (423) */ export interface PalletEvmContractHelpersSponsoringModeT extends Enum { readonly isDisabled: boolean; readonly isAllowlisted: boolean; @@ -3221,20 +3270,20 @@ declare module '@polkadot/types/lookup' { readonly type: 'Disabled' | 'Allowlisted' | 'Generous'; } - /** @name PalletEvmContractHelpersError (423) */ + /** @name PalletEvmContractHelpersError (425) */ export interface PalletEvmContractHelpersError extends Enum { readonly isNoPermission: boolean; readonly type: 'NoPermission'; } - /** @name PalletEvmMigrationError (424) */ + /** @name PalletEvmMigrationError (426) */ export interface PalletEvmMigrationError extends Enum { readonly isAccountNotEmpty: boolean; readonly isAccountIsNotMigrating: boolean; readonly type: 'AccountNotEmpty' | 'AccountIsNotMigrating'; } - /** @name SpRuntimeMultiSignature (426) */ + /** @name SpRuntimeMultiSignature (428) */ export interface SpRuntimeMultiSignature extends Enum { readonly isEd25519: boolean; readonly asEd25519: SpCoreEd25519Signature; @@ -3245,34 +3294,34 @@ declare module '@polkadot/types/lookup' { readonly type: 'Ed25519' | 'Sr25519' | 'Ecdsa'; } - /** @name SpCoreEd25519Signature (427) */ + /** @name SpCoreEd25519Signature (429) */ export interface SpCoreEd25519Signature extends U8aFixed {} - /** @name SpCoreSr25519Signature (429) */ + /** @name SpCoreSr25519Signature (431) */ export interface SpCoreSr25519Signature extends U8aFixed {} - /** @name SpCoreEcdsaSignature (430) */ + /** @name SpCoreEcdsaSignature (432) */ export interface SpCoreEcdsaSignature extends U8aFixed {} - /** @name FrameSystemExtensionsCheckSpecVersion (433) */ + /** @name FrameSystemExtensionsCheckSpecVersion (435) */ export type FrameSystemExtensionsCheckSpecVersion = Null; - /** @name FrameSystemExtensionsCheckGenesis (434) */ + /** @name FrameSystemExtensionsCheckGenesis (436) */ export type FrameSystemExtensionsCheckGenesis = Null; - /** @name FrameSystemExtensionsCheckNonce (437) */ + /** @name FrameSystemExtensionsCheckNonce (439) */ export interface FrameSystemExtensionsCheckNonce extends Compact {} - /** @name FrameSystemExtensionsCheckWeight (438) */ + /** @name FrameSystemExtensionsCheckWeight (440) */ export type FrameSystemExtensionsCheckWeight = Null; - /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (439) */ + /** @name PalletTemplateTransactionPaymentChargeTransactionPayment (441) */ export interface PalletTemplateTransactionPaymentChargeTransactionPayment extends Compact {} - /** @name OpalRuntimeRuntime (440) */ + /** @name OpalRuntimeRuntime (442) */ export type OpalRuntimeRuntime = Null; - /** @name PalletEthereumFakeTransactionFinalizer (441) */ + /** @name PalletEthereumFakeTransactionFinalizer (443) */ export type PalletEthereumFakeTransactionFinalizer = Null; } // declare module diff --git a/tests/src/nesting/graphs.test.ts b/tests/src/nesting/graphs.test.ts index a6ca0ca6c1..d384dddd00 100644 --- a/tests/src/nesting/graphs.test.ts +++ b/tests/src/nesting/graphs.test.ts @@ -36,7 +36,7 @@ describe('Graphs', () => { await usingApi(async (api, privateKeyWrapper) => { const alice = privateKeyWrapper('//Alice'); const collection = await buildComplexObjectGraph(api, alice); - await setCollectionLimitsExpectSuccess(alice, collection, {ownerCanTransfer: true}); + const tokenTwoParent = tokenIdToCross(collection, 1); // to self await expect( @@ -49,7 +49,7 @@ describe('Graphs', () => { 'second transaction', ).to.be.rejectedWith(/structure\.OuroborosDetected/); await expect( - executeTransaction(api, alice, api.tx.unique.transfer(tokenIdToCross(collection, 8), collection, 2, 1)), + executeTransaction(api, alice, api.tx.unique.transferFrom(tokenTwoParent, tokenIdToCross(collection, 8), collection, 2, 1)), 'third transaction', ).to.be.rejectedWith(/structure\.OuroborosDetected/); }); diff --git a/tests/src/nesting/nest.test.ts b/tests/src/nesting/nest.test.ts index 840cd1b28d..6db38e950c 100644 --- a/tests/src/nesting/nest.test.ts +++ b/tests/src/nesting/nest.test.ts @@ -123,7 +123,7 @@ describe('Integration Test: Composite nesting tests', () => { ], 'Children contents check at nesting #2'); // Move token B to a different user outside the nesting tree - await transferExpectSuccess(collectionA, tokenB, alice, bob); + await transferFromExpectSuccess(collectionA, tokenB, alice, targetAddress, bob); children = await getTokenChildren(api, collectionA, targetToken); expect(children.length).to.be.equal(1, 'Children length check at unnesting'); expect(children).to.be.have.deep.members([ @@ -470,7 +470,7 @@ describe('Negative Test: Nesting', async() => { await expect(executeTransaction( api, alice, - api.tx.unique.transfer(targetAddress, collection, newToken, 1), + api.tx.unique.transferFrom(targetAddress, {Substrate: bob.address}, collection, newToken, 1), ), 'while nesting another\'s token token').to.be.rejectedWith(/common\.AddressNotInAllowlist/); expect(await getTokenOwner(api, collection, newToken)).to.be.deep.equal({Substrate: bob.address}); diff --git a/tests/src/nesting/properties.test.ts b/tests/src/nesting/properties.test.ts index 4dac7fb1d3..ab0a88ee65 100644 --- a/tests/src/nesting/properties.test.ts +++ b/tests/src/nesting/properties.test.ts @@ -3,11 +3,13 @@ import usingApi, {executeTransaction} from '../substrate/substrate-api'; import { addCollectionAdminExpectSuccess, createCollectionExpectSuccess, + setCollectionPermissionsExpectSuccess, createItemExpectSuccess, getCreateCollectionResult, transferExpectSuccess, } from '../util/helpers'; import {IKeyringPair} from '@polkadot/types/types'; +import {tokenIdToAddress} from '../eth/util/helpers'; let alice: IKeyringPair; let bob: IKeyringPair; @@ -522,6 +524,7 @@ describe('Negative Integration Test: Access Rights to Token Properties', () => { describe('Integration Test: Token Properties', () => { let collection: number; let token: number; + let nestedToken: number; let permissions: {permission: any, signers: IKeyringPair[]}[]; before(async () => { @@ -544,7 +547,11 @@ describe('Integration Test: Token Properties', () => { beforeEach(async () => { await usingApi(async () => { collection = await createCollectionExpectSuccess(); + await setCollectionPermissionsExpectSuccess(alice, collection, {nesting: {tokenOwner: true}}); + token = await createItemExpectSuccess(alice, collection, 'NFT'); + nestedToken = await createItemExpectSuccess(alice, collection, 'NFT', {Ethereum: tokenIdToAddress(collection, token)}); + await addCollectionAdminExpectSuccess(alice, collection, bob.address); await transferExpectSuccess(collection, token, alice, charlie); }); @@ -681,6 +688,124 @@ describe('Integration Test: Token Properties', () => { expect((await api.query.nonfungible.tokenProperties(collection, token)).toJSON().consumedSpace).to.be.equal(0); }); }); + + it('Assigns properties to a nested token according to permissions', async () => { + await usingApi(async api => { + const propertyKeys: string[] = []; + let i = 0; + for (const permission of permissions) { + for (const signer of permission.signers) { + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect(executeTransaction( + api, + alice, + api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), + ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected; + + await expect(executeTransaction( + api, + signer, + api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), + ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected; + } + + i++; + } + + const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[]; + const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[]; + for (let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin increase'); + expect(tokensData[i].value).to.be.equal('Serotonin increase'); + } + }); + }); + + it('Changes properties of a nested token according to permissions', async () => { + await usingApi(async api => { + const propertyKeys: string[] = []; + let i = 0; + for (const permission of permissions) { + if (!permission.permission.mutable) continue; + + for (const signer of permission.signers) { + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect(executeTransaction( + api, + alice, + api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), + ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected; + + await expect(executeTransaction( + api, + signer, + api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), + ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected; + + await expect(executeTransaction( + api, + signer, + api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin stable'}]), + ), `on changing property ${i} by ${signer.address}`).to.not.be.rejected; + } + + i++; + } + + const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toHuman() as any[]; + const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toHuman().properties as any[]; + for (let i = 0; i < properties.length; i++) { + expect(properties[i].value).to.be.equal('Serotonin stable'); + expect(tokensData[i].value).to.be.equal('Serotonin stable'); + } + }); + }); + + it('Deletes properties of a nested token according to permissions', async () => { + await usingApi(async api => { + const propertyKeys: string[] = []; + let i = 0; + + for (const permission of permissions) { + if (!permission.permission.mutable) continue; + + for (const signer of permission.signers) { + const key = i + '_' + signer.address; + propertyKeys.push(key); + + await expect(executeTransaction( + api, + alice, + api.tx.unique.setTokenPropertyPermissions(collection, [{key: key, permission: permission.permission}]), + ), `on setting permission ${i} by ${signer.address}`).to.not.be.rejected; + + await expect(executeTransaction( + api, + signer, + api.tx.unique.setTokenProperties(collection, nestedToken, [{key: key, value: 'Serotonin increase'}]), + ), `on adding property ${i} by ${signer.address}`).to.not.be.rejected; + + await expect(executeTransaction( + api, + signer, + api.tx.unique.deleteTokenProperties(collection, nestedToken, [key]), + ), `on deleting property ${i} by ${signer.address}`).to.not.be.rejected; + } + + i++; + } + + const properties = (await api.rpc.unique.tokenProperties(collection, nestedToken, propertyKeys)).toJSON() as any[]; + expect(properties).to.be.empty; + const tokensData = (await api.rpc.unique.tokenData(collection, nestedToken, propertyKeys)).toJSON().properties as any[]; + expect(tokensData).to.be.empty; + expect((await api.query.nonfungible.tokenProperties(collection, nestedToken)).toJSON().consumedSpace).to.be.equal(0); + }); + }); }); describe('Negative Integration Test: Token Properties', () => { @@ -848,4 +973,4 @@ describe('Negative Integration Test: Token Properties', () => { expect(propertiesMap.consumedSpace).to.be.equal(originalSpace); }); }); -}); \ No newline at end of file +}); diff --git a/tests/src/refungible.test.ts b/tests/src/refungible.test.ts index 84908b1fc2..72890520d7 100644 --- a/tests/src/refungible.test.ts +++ b/tests/src/refungible.test.ts @@ -29,6 +29,7 @@ import { createRefungibleToken, transfer, burnItem, + repartitionRFT, } from './util/helpers'; import chai from 'chai'; @@ -162,4 +163,27 @@ describe('integration test: Refungible functionality:', () => { expect(await getAllowance(api, collectionId, alice, bob, tokenId)).to.be.equal(40n); }); }); + + it('Repartition', async () => { + await usingApi(async api => { + const collectionId = (await createCollection(api, alice, {mode: {type: 'ReFungible'}})).collectionId; + const tokenId = (await createRefungibleToken(api, alice, collectionId, 100n)).itemId; + + expect(await repartitionRFT(api, collectionId, alice, tokenId, 200n)).to.be.true; + expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(200n); + + expect(await transfer(api, collectionId, tokenId, alice, bob, 110n)).to.be.true; + expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(90n); + expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(110n); + + await expect(repartitionRFT(api, collectionId, alice, tokenId, 80n)).to.eventually.be.rejected; + + expect(await transfer(api, collectionId, tokenId, alice, bob, 90n)).to.be.true; + expect(await getBalance(api, collectionId, alice, tokenId)).to.be.equal(0n); + expect(await getBalance(api, collectionId, bob, tokenId)).to.be.equal(200n); + + expect(await repartitionRFT(api, collectionId, bob, tokenId, 150n)).to.be.true; + await expect(transfer(api, collectionId, tokenId, bob, alice, 160n)).to.eventually.be.rejected; + }); + }); }); diff --git a/tests/src/rmrk/acceptNft.test.ts b/tests/src/rmrk/acceptNft.test.ts new file mode 100644 index 0000000000..f3e50f65e7 --- /dev/null +++ b/tests/src/rmrk/acceptNft.test.ts @@ -0,0 +1,103 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import { + createCollection, + mintNft, + sendNft, + acceptNft, +} from './util/tx'; +import {NftIdTuple} from './util/fetch'; +import {isNftChildOfAnother, expectTxFailure} from './util/helpers'; + +describe('integration test: accept NFT', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + const createTestCollection = async (issuerUri: string) => { + return await createCollection( + api, + issuerUri, + 'accept-metadata', + null, + 'acpt', + ); + }; + + it('accept NFT', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + await sendNft(api, 'pending', ownerBob, bobCollectionId, childNftId, newOwnerNFT); + await acceptNft(api, alice, bobCollectionId, childNftId, newOwnerNFT); + + const isChild = await isNftChildOfAnother(api, bobCollectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.true; + }); + + it('[negative] unable to accept NFT by a not-an-owner', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + await sendNft(api, 'pending', ownerBob, bobCollectionId, childNftId, newOwnerNFT); + const tx = acceptNft(api, bob, bobCollectionId, childNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative] unable to accept non-existing NFT', async () => { + const collectionId = 0; + const maxNftId = 0xFFFFFFFF; + + const owner = alice; + const aliceCollectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, owner, aliceCollectionId, 'parent-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + const tx = acceptNft(api, alice, collectionId, maxNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] unable to accept NFT which is not sent', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + const tx = acceptNft(api, alice, bobCollectionId, childNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + + const isChild = await isNftChildOfAnother(api, bobCollectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.false; + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/addResource.test.ts b/tests/src/rmrk/addResource.test.ts new file mode 100644 index 0000000000..9798152053 --- /dev/null +++ b/tests/src/rmrk/addResource.test.ts @@ -0,0 +1,436 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {NftIdTuple} from './util/fetch'; +import {expectTxFailure, getResourceById} from './util/helpers'; +import { + addNftBasicResource, + acceptNftResource, + createCollection, + mintNft, + sendNft, + addNftSlotResource, + addNftComposableResource, +} from './util/tx'; +import {RmrkTraitsResourceResourceInfo as ResourceInfo} from '@polkadot/types/lookup'; + +describe('integration test: add NFT resource', () => { + const Alice = '//Alice'; + const Bob = '//Bob'; + const src = 'test-res-src'; + const metadata = 'test-res-metadata'; + const license = 'test-res-license'; + const thumb = 'test-res-thumb'; + + const nonexistentId = 99999; + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('add resource', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + }); + + it('add a resource to the nested NFT', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'parent-nft-metadata'); + const childNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionIdAlice, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionIdAlice, childNftId, newOwnerNFT); + + await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + childNftId, + src, + metadata, + license, + thumb, + ); + }); + + it('add multiple resources', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const baseId = 42; + const slotId = 10; + const parts = [0, 5, 2]; + + const resourcesInfo = []; + const resourceNum = 4; + + const checkResource = async (resource: ResourceInfo, resType: string, expectedId: number, expected: { + src: string, + metadata: string, + license: string, + thumb: string + }) => { + + // FIXME A workaround. It seems it is a PolkadotJS bug. + // All of the following are `false`. + // + // console.log('>>> basic:', resource.resource.isBasic); + // console.log('>>> composable:', resource.resource.isComposable); + // console.log('>>> slot:', resource.resource.isSlot); + const resourceJson = (resource.resource.toHuman() as any)[resType]; + + expect(resource.id.toNumber(), 'Error: Invalid resource Id') + .to.be.eq(expectedId); + + expect(resourceJson.src, 'Error: Invalid resource src') + .to.be.eq(expected.src); + expect(resourceJson.metadata, 'Error: Invalid resource metadata') + .to.be.eq(expected.metadata); + expect(resourceJson.license, 'Error: Invalid resource license') + .to.be.eq(expected.license); + expect(resourceJson.thumb, 'Error: Invalid resource thumb') + .to.be.eq(expected.thumb); + }; + + for (let i = 0; i < resourceNum; i++) { + resourcesInfo.push({ + src: src + 'r-' + i, + metadata: metadata + 'r-' + i, + license: license + 'r-' + i, + thumb: thumb + 'r-' + i, + }); + } + + const firstBasicResourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + resourcesInfo[0].src, + resourcesInfo[0].metadata, + resourcesInfo[0].license, + resourcesInfo[0].thumb, + ); + + const secondBasicResourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + resourcesInfo[1].src, + resourcesInfo[1].metadata, + resourcesInfo[1].license, + resourcesInfo[1].thumb, + ); + + const composableResourceId = await addNftComposableResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + parts, + baseId, + resourcesInfo[2].src, + resourcesInfo[2].metadata, + resourcesInfo[2].license, + resourcesInfo[2].thumb, + ); + + const slotResourceId = await addNftSlotResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + baseId, + slotId, + resourcesInfo[3].src, + resourcesInfo[3].metadata, + resourcesInfo[3].license, + resourcesInfo[3].thumb, + ); + + const firstResource = await getResourceById(api, collectionIdAlice, nftAlice, firstBasicResourceId); + await checkResource(firstResource, 'Basic', firstBasicResourceId, resourcesInfo[0]); + + const secondResource = await getResourceById(api, collectionIdAlice, nftAlice, secondBasicResourceId); + await checkResource(secondResource, 'Basic', secondBasicResourceId, resourcesInfo[1]); + + const composableResource = await getResourceById(api, collectionIdAlice, nftAlice, composableResourceId); + await checkResource(composableResource, 'Composable', composableResourceId, resourcesInfo[2]); + + const slotResource = await getResourceById(api, collectionIdAlice, nftAlice, slotResourceId); + await checkResource(slotResource, 'Slot', slotResourceId, resourcesInfo[3]); + }); + + it('[negative]: unable to add a resource to the non-existing NFT', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const tx = addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nonexistentId, + src, + metadata, + license, + thumb, + ); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative]: unable to add a resource by a not-an-owner user', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const tx = addNftBasicResource( + api, + Bob, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative]: unable to add a resource to the nested NFT if it isnt root owned by the caller', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'parent-nft-metadata'); + const childNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionIdAlice, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionIdAlice, childNftId, newOwnerNFT); + + const tx = addNftBasicResource( + api, + Bob, + 'added', + collectionIdAlice, + childNftId, + src, + metadata, + license, + thumb, + ); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('accept resource', async () => { + const collectionIdBob = await createCollection( + api, + Bob, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Bob, + Alice, + collectionIdBob, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Bob, + 'pending', + collectionIdBob, + nftAlice, + src, + metadata, + license, + thumb, + ); + + await acceptNftResource(api, Alice, collectionIdBob, nftAlice, resourceId); + }); + + it('[negative]: unable to accept a non-existing resource', async () => { + const collectionIdBob = await createCollection( + api, + Bob, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Bob, + Alice, + collectionIdBob, + 'nft-metadata', + ); + + const tx = acceptNftResource(api, Alice, collectionIdBob, nftAlice, nonexistentId); + await expectTxFailure(/rmrkCore\.ResourceDoesntExist/, tx); + }); + + it('[negative]: unable to accept a resource by a not-an-NFT-owner user', async () => { + const collectionIdBob = await createCollection( + api, + Bob, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Bob, + Alice, + collectionIdBob, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Bob, + 'pending', + collectionIdBob, + nftAlice, + src, + metadata, + license, + thumb, + ); + + const tx = acceptNftResource(api, Bob, collectionIdBob, nftAlice, resourceId); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative]: unable to accept a resource to a non-target NFT', async () => { + const collectionIdBob = await createCollection( + api, + Bob, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Bob, + Alice, + collectionIdBob, + 'nft-metadata', + ); + + const wrongNft = await mintNft( + api, + Bob, + Alice, + collectionIdBob, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Bob, + 'pending', + collectionIdBob, + nftAlice, + src, + metadata, + license, + thumb, + ); + + const tx = acceptNftResource(api, Bob, collectionIdBob, wrongNft, resourceId); + + await expectTxFailure(/rmrkCore\.ResourceDoesntExist/, tx); + }); + + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/addTheme.test.ts b/tests/src/rmrk/addTheme.test.ts new file mode 100644 index 0000000000..b2bb56e0ff --- /dev/null +++ b/tests/src/rmrk/addTheme.test.ts @@ -0,0 +1,126 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {createBase, addTheme} from './util/tx'; +import {expectTxFailure} from './util/helpers'; +import {getThemeNames} from './util/fetch'; + +describe('integration test: add Theme to Base', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + it('add default theme', async () => { + const baseId = await createBase(api, alice, 'default-themed-base', 'DTBase', []); + await addTheme(api, alice, baseId, { + name: 'default', + properties: [ + { + key: 'some-key', + value: 'some-key-value', + }, + { + key: 'another-key', + value: 'another-key-value', + }, + ], + }); + }); + + it('add default theme and a custom one', async () => { + const baseId = await createBase(api, alice, '2-themed-base', '2TBase', []); + await addTheme(api, alice, baseId, { + name: 'default', + properties: [ + { + key: 'default-key', + value: 'default-key-value', + }, + ], + }); + await addTheme(api, alice, baseId, { + name: 'custom-theme', + properties: [ + { + key: 'custom-key-0', + value: 'custom-key-value-0', + }, + { + key: 'custom-key-1', + value: 'custom-key-value-1', + }, + ], + }); + }); + + it('fetch filtered theme keys', async () => { + const baseId = await createBase(api, alice, '2-themed-base', '2TBase', []); + await addTheme(api, alice, baseId, { + name: 'default', + properties: [ + { + key: 'first-key', + value: 'first-key-value', + }, + { + key: 'second-key', + value: 'second-key-value', + }, + ], + }, ['second-key']); + }); + + it('fetch theme names', async() => { + const baseId = await createBase(api, alice, '3-themed-base', '3TBase', []); + const names = [ + 'default', + 'first-theme', + 'second-theme', + ]; + + for (let i = 0; i < names.length; i++) { + await addTheme(api, alice, baseId, {name: names[i], properties: [{key: 'dummy', value: 'dummy'}]}); + } + + const fetchedNames = await getThemeNames(api, baseId); + + for (let i = 0; i < names.length; i++) { + const isFound = fetchedNames.find((name) => name === names[i]) !== undefined; + + expect(isFound, 'Error: invalid theme names').to.be.true; + } + }); + + it('[negative] unable to add theme to non-existing base', async () => { + const maxBaseId = 0xFFFFFFFF; + const tx = addTheme(api, alice, maxBaseId, { + name: 'default', + properties: [], + }); + + await expectTxFailure(/rmrkEquip\.BaseDoesntExist/, tx); + }); + + it('[negative] unable to add custom theme if no default theme', async () => { + const baseId = await createBase(api, alice, 'no-default-themed-base', 'NDTBase', []); + const tx = addTheme(api, alice, baseId, { + name: 'custom-theme', + properties: [], + }); + + await expectTxFailure(/rmrkEquip\.NeedsDefaultThemeFirst/, tx); + }); + + it('[negative] unable to add theme by a not-an-owner', async () => { + const baseId = await createBase(api, alice, 'no-default-themed-base', 'NDTBase', []); + const tx = addTheme(api, bob, baseId, { + name: 'default', + properties: [], + }); + + await expectTxFailure(/rmrkEquip\.PermissionError/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/burnNft.test.ts b/tests/src/rmrk/burnNft.test.ts new file mode 100644 index 0000000000..c640e625a6 --- /dev/null +++ b/tests/src/rmrk/burnNft.test.ts @@ -0,0 +1,170 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {NftIdTuple, getChildren} from './util/fetch'; +import {burnNft, createCollection, sendNft, mintNft} from './util/tx'; + +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +describe('integration test: burn nft', () => { + const Alice = '//Alice'; + const Bob = '//Bob'; + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('burn nft', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const nftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + await burnNft(api, Alice, collectionId, nftId); + }); + }); + + it('burn nft with children', async () => { + const collectionId = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const childNftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionId, childNftId, newOwnerNFT); + + const childrenBefore = await getChildren(api, collectionId, parentNftId); + expect(childrenBefore.length === 1, 'Error: parent NFT should have children') + .to.be.true; + + const child = childrenBefore[0]; + expect(child.collectionId.eq(collectionId), 'Error: invalid child collection Id') + .to.be.true; + + expect(child.nftId.eq(childNftId), 'Error: invalid child NFT Id') + .to.be.true; + + await burnNft(api, Alice, collectionId, parentNftId); + + const childrenAfter = await getChildren(api, collectionId, parentNftId); + + expect(childrenAfter.length === 0, 'Error: children should be burned').to.be.true; + }); + + it('burn child nft', async () => { + const collectionId = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const childNftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionId, childNftId, newOwnerNFT); + + const childrenBefore = await getChildren(api, collectionId, parentNftId); + expect(childrenBefore.length === 1, 'Error: parent NFT should have children') + .to.be.true; + + const child = childrenBefore[0]; + expect(child.collectionId.eq(collectionId), 'Error: invalid child collection Id') + .to.be.true; + + expect(child.nftId.eq(childNftId), 'Error: invalid child NFT Id') + .to.be.true; + + await burnNft(api, Alice, collectionId, childNftId); + + const childrenAfter = await getChildren(api, collectionId, parentNftId); + + expect(childrenAfter.length === 0, 'Error: children should be burned').to.be.true; + }); + + it('[negative] burn non-existing NFT', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const tx = burnNft(api, Alice, collectionId, 99999); + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + }); + + it('[negative] burn not an owner NFT user', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const nftId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + const tx = burnNft(api, Bob, collectionId, nftId); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/changeCollectionIssuer.test.ts b/tests/src/rmrk/changeCollectionIssuer.test.ts new file mode 100644 index 0000000000..6d1afec7b0 --- /dev/null +++ b/tests/src/rmrk/changeCollectionIssuer.test.ts @@ -0,0 +1,52 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import { + changeIssuer, + createCollection, +} from './util/tx'; + +describe('integration test: collection issuer', () => { + const Alice = '//Alice'; + const Bob = '//Bob'; + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('change collection issuer', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + await changeIssuer(api, Alice, collectionId, Bob); + }); + }); + + it('[negative] change not an owner NFT collection issuer', async () => { + await createCollection(api, Bob, 'test-metadata', null, 'test-symbol').then(async (collectionId) => { + const tx = changeIssuer(api, Alice, collectionId, Bob); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + }); + + it('[negative] change non-existigit NFT collection issuer', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async () => { + const tx = changeIssuer(api, Alice, 99999, Bob); + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); + }); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/createBase.test.ts b/tests/src/rmrk/createBase.test.ts new file mode 100644 index 0000000000..5d07489aa9 --- /dev/null +++ b/tests/src/rmrk/createBase.test.ts @@ -0,0 +1,84 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {createCollection, createBase} from './util/tx'; + +describe('integration test: create new Base', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + + it('create empty Base', async () => { + await createBase(api, alice, 'empty-base-type', 'EBase', []); + }); + + it('create Base with fixed part', async () => { + await createBase(api, alice, 'fixedpart-base-type', 'FPBase', [ + { + 'FixedPart': { + id: 42, + z: 0, + src: 'some-fixed-url', + }, + }, + ]); + }); + + it('create Base with slot part (no collection)', async () => { + await createBase(api, alice, 'slotpart-base-type', 'SPBase', [ + { + 'SlotPart': { + id: 112, + equippable: 'Empty', + z: 0, + src: 'some-fallback-slot-url', + }, + }, + ]); + }); + + it('create Base with slot part (any collection)', async () => { + await createBase(api, alice, 'slotpartany-base-type', 'SPABase', [ + { + 'SlotPart': { + id: 222, + equippable: 'All', + z: 1, + src: 'some-fallback-slot-url', + }, + }, + ]); + }); + + it('create Base with slot part (custom collections)', async () => { + const firstCollectionId = await createCollection( + api, + alice, + 'first-collection-meta', + null, + 'first-collection', + ); + + const secondCollectionId = await createCollection( + api, + alice, + 'first-collection-meta', + null, + 'first-collection', + ); + + await createBase(api, alice, 'slotpartcustom-base-type', 'SPCBase', [ + { + 'SlotPart': { + id: 1024, + equippable: { + 'Custom': [firstCollectionId, secondCollectionId], + }, + z: 2, + src: 'some-fallback-slot-url', + }, + }, + ]); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/createCollection.test.ts b/tests/src/rmrk/createCollection.test.ts new file mode 100644 index 0000000000..22c879b2b2 --- /dev/null +++ b/tests/src/rmrk/createCollection.test.ts @@ -0,0 +1,19 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {createCollection} from './util/tx'; + +describe('Integration test: create new collection', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + + it('create NFT collection', async () => { + await createCollection(api, alice, 'test-metadata', 42, 'test-symbol'); + }); + + it('create NFT collection without token limit', async () => { + await createCollection(api, alice, 'no-limit-metadata', null, 'no-limit-symbol'); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/deleteCollection.test.ts b/tests/src/rmrk/deleteCollection.test.ts new file mode 100644 index 0000000000..a6925c2c5b --- /dev/null +++ b/tests/src/rmrk/deleteCollection.test.ts @@ -0,0 +1,47 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, deleteCollection} from './util/tx'; + +describe('integration test: delete collection', () => { + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + const Alice = '//Alice'; + const Bob = '//Bob'; + + it('delete NFT collection', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + await deleteCollection(api, Alice, collectionId.toString()); + }); + }); + + it('[negative] delete non-existing NFT collection', async () => { + const tx = deleteCollection(api, Alice, '99999'); + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); + }); + + it('[negative] delete not an owner NFT collection', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const tx = deleteCollection(api, Bob, collectionId.toString()); + await expectTxFailure(/rmrkCore.NoPermission/, tx); + }); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/equipNft.test.ts b/tests/src/rmrk/equipNft.test.ts new file mode 100644 index 0000000000..76a8a988dd --- /dev/null +++ b/tests/src/rmrk/equipNft.test.ts @@ -0,0 +1,340 @@ +import {ApiPromise} from '@polkadot/api'; +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {getNft, getParts, NftIdTuple} from './util/fetch'; +import {expectTxFailure} from './util/helpers'; +import { + addNftComposableResource, + addNftSlotResource, + createBase, + createCollection, + equipNft, + mintNft, + sendNft, + unequipNft, +} from './util/tx'; + +const Alice = '//Alice'; +const Bob = '//Bob'; + +const composableParts: number[] = [5, 2, 7]; +const composableSrc = 'test-cmp-src'; +const composableMetadata = 'test-cmp-metadata'; +const composableLicense = 'test-comp-license'; +const composableThumb = 'test-comp-thumb'; + +const slotSrc = 'test-slot-src'; +const slotLicense = 'test-slot-license'; +const slotMetadata = 'test-slot-metadata'; +const slotThumb = 'test-slot-thumb'; + +const slotId = 1; + +async function createTestCollection(api: ApiPromise): Promise { + return createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); +} + +async function mintTestNft(api: ApiPromise, collectionId: number): Promise { + return await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); +} + +async function mintChildNft(api: ApiPromise, collectionId: number, parentNftId: number): Promise { + const nftChildId = await mintTestNft(api, collectionId); + + const parentNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionId, nftChildId, parentNFT); + + return nftChildId; +} + +async function createTestBase(api: ApiPromise): Promise { + return createBase(api, Alice, 'test-base', 'DTBase', [ + { + SlotPart: { + id: slotId, + equippable: 'All', + z: 1, + src: slotSrc, + }, + }, + ]); +} + +async function addTestComposable(api: ApiPromise, collectionId: number, nftId: number, baseId: number) { + await addNftComposableResource( + api, + Alice, + 'added', + collectionId, + nftId, + composableParts, + baseId, + composableSrc, + composableMetadata, + composableLicense, + composableThumb, + ); +} + +async function addTestSlot(api: ApiPromise, collectionId: number, nftId: number, baseId: number, slotId: number): Promise { + return await addNftSlotResource( + api, + Alice, + 'added', + collectionId, + nftId, + baseId, + slotId, + slotSrc, + slotMetadata, + slotLicense, + slotThumb, + ); +} + +async function checkEquipStatus( + api: ApiPromise, + expectedStatus: 'equipped' | 'unequipped', + collectionId: number, + nftId: number, +) { + const itemNftDataOpt = await getNft(api, collectionId, nftId); + expect(itemNftDataOpt.isSome, 'Error: unable to fetch item NFT data'); + + const itemNftData = itemNftDataOpt.unwrap(); + expect(itemNftData.equipped.isTrue, `Error: item NFT should be ${expectedStatus}`) + .to.be.equal(expectedStatus === 'equipped'); +} + +describe.skip('integration test: Equip NFT', () => { + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('equip nft', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + await equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + + await checkEquipStatus(api, 'equipped', collectionId, nftChildId); + }); + + it('unequip nft', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + await equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + + await checkEquipStatus(api, 'equipped', collectionId, nftChildId); + + await unequipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + await checkEquipStatus(api, 'unequipped', collectionId, nftChildId); + }); + + it('[negative] equip NFT onto non-existing NFT', async () => { + const collectionId = await createTestCollection(api); + + const nftChildId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + const invalidEquipperNFT: NftIdTuple = [collectionId, 9999999]; + + const baseId = 0; + const resourceId = 0; + + const tx = equipNft(api, Alice, itemNFT, invalidEquipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] equip non-existing NFT', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintNft( + api, + Alice, + Alice, + collectionId, + 'nft-metadata', + ); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const invalidItemNFT: NftIdTuple = [collectionId, 99999999]; + + const resourceId = 0; + + const tx = equipNft(api, Alice, invalidItemNFT, equipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] equip NFT by a not-an-owner user', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const tx = equipNft(api, Bob, itemNFT, equipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkEquip\.PermissionError/, tx); + }); + + it('[negative] unable to equip NFT onto indirect parent NFT', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + const nftGrandchildId = await mintChildNft(api, collectionId, nftChildId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftGrandchildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftGrandchildId]; + + const tx = equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkEquip\.MustBeDirectParent/, tx); + }); + + it('[negative] unable to equip NFT onto parent NFT with another base', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + const invalidBaseId = 99999; + + const tx = equipNft(api, Alice, itemNFT, equipperNFT, resourceId, invalidBaseId, slotId); + await expectTxFailure(/rmrkEquip\.NoResourceForThisBaseFoundOnNft/, tx); + }); + + it('[negative] unable to equip NFT into slot with another id', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createTestBase(api); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + const incorrectSlotId = slotId + 1; + const tx = equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, incorrectSlotId); + await expectTxFailure(/rmrkEquip\.ItemHasNoResourceToEquipThere/, tx); + }); + + it('[negative] unable to equip NFT with incorrect slot (fixed part)', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createBase(api, Alice, 'test-base', 'DTBase', [ + { + FixedPart: { + id: slotId, + equippable: 'All', + z: 1, + src: slotSrc, + }, + }, + ]); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + const tx = equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkEquip\.CantEquipFixedPart/, tx); + }); + + it('[negative] unable to equip NFT from a collection that is not allowed by the slot', async () => { + const collectionId = await createTestCollection(api); + const nftParentId = await mintTestNft(api, collectionId); + const nftChildId = await mintChildNft(api, collectionId, nftParentId); + + const baseId = await createBase(api, Alice, 'test-base', 'DTBase', [ + { + SlotPart: { + id: 1, + z: 1, + equippable: 'Empty', + src: slotSrc, + }, + }, + ]); + + await addTestComposable(api, collectionId, nftParentId, baseId); + const resourceId = await addTestSlot(api, collectionId, nftChildId, baseId, slotId); + + const equipperNFT: NftIdTuple = [collectionId, nftParentId]; + const itemNFT: NftIdTuple = [collectionId, nftChildId]; + + const tx = equipNft(api, Alice, itemNFT, equipperNFT, resourceId, baseId, slotId); + await expectTxFailure(/rmrkEquip\.CollectionNotEquippable/, tx); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/getOwnedNfts.test.ts b/tests/src/rmrk/getOwnedNfts.test.ts new file mode 100644 index 0000000000..6fbf5499d1 --- /dev/null +++ b/tests/src/rmrk/getOwnedNfts.test.ts @@ -0,0 +1,74 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {getOwnedNfts} from './util/fetch'; +import {mintNft, createCollection} from './util/tx'; + +describe('integration test: get owned NFTs', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + + it('fetch all NFTs owned by a user', async () => { + const owner = alice; + const collectionMetadata = 'aliceCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'AliceSym'; + const recipientUri = null; + const royalty = null; + const nftMetadata = 'alice-NFT-metadata'; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + const nftIds = [ + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata + '-0', + recipientUri, + royalty, + ), + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata + '-1', + recipientUri, + royalty, + ), + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata + '-2', + recipientUri, + royalty, + ), + ]; + + const ownedNfts = await getOwnedNfts(api, alice, collectionId); + + const isFound = (nftId: number) => { + return ownedNfts.find((ownedNftId) => { + return ownedNftId === nftId; + }) !== undefined; + }; + + nftIds.forEach((nftId) => { + expect(isFound(nftId), `NFT ${nftId} should be owned by ${alice}`) + .to.be.true; + }); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/lockCollection.test.ts b/tests/src/rmrk/lockCollection.test.ts new file mode 100644 index 0000000000..f23294a7e5 --- /dev/null +++ b/tests/src/rmrk/lockCollection.test.ts @@ -0,0 +1,117 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, lockCollection, mintNft} from './util/tx'; + +describe('integration test: lock collection', () => { + const Alice = '//Alice'; + const Bob = '//Bob'; + const Max = 5; + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('lock collection', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + await lockCollection(api, Alice, collectionId); + }); + }); + + it('[negative] lock non-existing NFT collection', async () => { + const tx = lockCollection(api, Alice, 99999); + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); + }); + + it('[negative] lock not an owner NFT collection issuer', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const tx = lockCollection(api, Bob, collectionId); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + }); + + it('lock collection with minting', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + Max, + 'test-symbol', + ).then(async (collectionId) => { + for (let i = 0; i < 5; i++) { + await mintNft( + api, + Alice, + Alice, + collectionId, + 'test-metadata', + null, + null, + ); + } + await lockCollection(api, Alice, collectionId, Max); + }); + }); + + it('[negative] unable to mint NFT inside a locked collection', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + Max, + 'test-symbol', + ).then(async (collectionId) => { + await lockCollection(api, Alice, collectionId); + const tx = mintNft( + api, + Alice, + Alice, + collectionId, + 'test-metadata', + null, + null, + ); + await expectTxFailure(/rmrkCore\.CollectionFullOrLocked/, tx); + }); + }); + + it('[negative] unable to mint NFT inside a full collection', async () => { + await createCollection(api, Alice, 'test-metadata', 1, 'test-symbol').then(async (collectionId) => { + await mintNft( + api, + Alice, + Alice, + collectionId, + 'test-metadata', + null, + null, + ); + const tx = mintNft( + api, + Alice, + Alice, + collectionId, + 'test-metadata', + null, + null, + ); + await expectTxFailure(/rmrkCore\.CollectionFullOrLocked/, tx); + }); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/mintNft.test.ts b/tests/src/rmrk/mintNft.test.ts new file mode 100644 index 0000000000..6f77b4ea7f --- /dev/null +++ b/tests/src/rmrk/mintNft.test.ts @@ -0,0 +1,206 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {getNft} from './util/fetch'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, mintNft} from './util/tx'; + +describe('integration test: mint new NFT', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + const maxCollectionId = 0xFFFFFFFF; + const maxNftId = 0xFFFFFFFF; + + it('mint NFT', async () => { + const owner = null; + const collectionMetadata = 'mintingCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'MCS'; + const recipientUri = null; + const royalty = null; + const nftMetadata = 'NFT-test-metadata'; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata, + recipientUri, + royalty, + ); + }); + + it('mint NFT and set another owner', async () => { + const owner = bob; + const collectionMetadata = 'setOwnerCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'SOCS'; + const recipientUri = null; + const royalty = null; + const nftMetadata = 'setOwner-NFT-metadata'; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata, + recipientUri, + royalty, + ); + }); + + it('mint NFT with recipient and roalty', async () => { + const owner = alice; + const collectionMetadata = 'mintingCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'MCS'; + const recipientUri = bob; + const royalty = 70000; + const nftMetadata = 'recipient-royalty-NFT-test-metadata'; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata, + recipientUri, + royalty, + ); + }); + + it('mint NFT with resources', async () => { + const owner = alice; + const collectionMetadata = 'mintingCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'MCS'; + const nftMetadata = 'NFT-with-resources-test-metadata'; + const resources = [ + { + basic: { + metadata: 'basic-resource-nft-minting', + }, + }, { + slot: { + metadata: 'slot-resource-nft-minting', + slot: 9, + }, + }, { + composable: { + metadata: 'composable-resource-nft-minting', + parts: [0, 5, 2], + }, + }, { + slot: { + metadata: 'slot-resource-nft-minting-2', + thumb: 'srnm2', + base: 5, + }, + }, + ]; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + await mintNft( + api, + alice, + owner, + collectionId, + nftMetadata, + null, + null, + true, + resources, + ); + }); + + it('[negative] unable to mint NFT within non-existing collection', async () => { + const owner = alice; + const recipientUri = null; + const royalty = null; + const nftMetadata = 'NFT-test-metadata'; + + const tx = mintNft( + api, + alice, + owner, + maxCollectionId, + nftMetadata, + recipientUri, + royalty, + ); + + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); + }); + + it("[negative] unable to mint NFT by a user that isn't the owner of the collection", async () => { + const owner = alice; + const collectionMetadata = 'mintingCollectionMetadata'; + const collectionMax = null; + const collectionSymbol = 'MCS'; + const recipientUri = null; + const royalty = null; + const nftMetadata = 'NFT-test-metadata'; + + const collectionId = await createCollection( + api, + alice, + collectionMetadata, + collectionMax, + collectionSymbol, + ); + + const tx = mintNft( + api, + bob, + owner, + collectionId, + nftMetadata, + recipientUri, + royalty, + ); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative] unable to fetch non-existing NFT', async () => { + const nft = await getNft(api, maxCollectionId, maxNftId); + expect(nft.isSome).to.be.false; + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/rejectNft.test.ts b/tests/src/rmrk/rejectNft.test.ts new file mode 100644 index 0000000000..893866c8b3 --- /dev/null +++ b/tests/src/rmrk/rejectNft.test.ts @@ -0,0 +1,89 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import { + createCollection, + mintNft, + sendNft, + rejectNft, +} from './util/tx'; +import {getChildren, NftIdTuple} from './util/fetch'; +import {isNftChildOfAnother, expectTxFailure} from './util/helpers'; + +describe('integration test: reject NFT', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + const createTestCollection = async (issuerUri: string) => { + return await createCollection( + api, + issuerUri, + 'reject-metadata', + null, + 'rjct', + ); + }; + + it('reject NFT', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + await sendNft(api, 'pending', ownerBob, bobCollectionId, childNftId, newOwnerNFT); + await rejectNft(api, alice, bobCollectionId, childNftId); + + const isChild = await isNftChildOfAnother(api, bobCollectionId, childNftId, newOwnerNFT); + expect(isChild, 'Error: rejected NFT is still a child of the target NFT').to.be.false; + }); + + it('[negative] unable to reject NFT by a not-an-owner', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + await sendNft(api, 'pending', ownerBob, bobCollectionId, childNftId, newOwnerNFT); + const tx = rejectNft(api, bob, bobCollectionId, childNftId); + + await expectTxFailure(/rmrkCore\.CannotRejectNonOwnedNft/, tx); + }); + + it('[negative] unable to reject non-existing NFT', async () => { + const maxNftId = 0xFFFFFFFF; + + const collectionId = await createTestCollection(alice); + + const tx = rejectNft(api, alice, collectionId, maxNftId); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] unable to reject NFT which is not sent', async () => { + const ownerAlice = alice; + + const collectionId = await createTestCollection(alice); + + const nftId = await mintNft(api, alice, ownerAlice, collectionId, 'parent-nft-metadata'); + + const tx = rejectNft(api, alice, collectionId, nftId); + + await expectTxFailure(/rmrkCore\.CannotRejectNonPendingNft/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/removeResource.test.ts b/tests/src/rmrk/removeResource.test.ts new file mode 100644 index 0000000000..a76c5da796 --- /dev/null +++ b/tests/src/rmrk/removeResource.test.ts @@ -0,0 +1,348 @@ +import {expect} from 'chai'; +import privateKey from '../substrate/privateKey'; +import {executeTransaction, getApiConnection} from '../substrate/substrate-api'; +import {getNft, NftIdTuple} from './util/fetch'; +import {expectTxFailure} from './util/helpers'; +import { + acceptNft, acceptResourceRemoval, addNftBasicResource, + createBase, + createCollection, + mintNft, removeNftResource, sendNft, +} from './util/tx'; + + + + +describe('Integration test: remove nft resource', () => { + let api: any; + let ss58Format: string; + before(async () => { + api = await getApiConnection(); + ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + }); + + const Alice = '//Alice'; + const Bob = '//Bob'; + const src = 'test-basic-src'; + const metadata = 'test-basic-metadata'; + const license = 'test-basic-license'; + const thumb = 'test-basic-thumb'; + + it('deleting a resource directly by the NFT owner', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + + await removeNftResource(api, 'removed', Alice, collectionIdAlice, nftAlice, resourceId); + }); + + it('deleting resources indirectly by the NFT owner', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'parent-nft-metadata'); + const childNftId = await mintNft(api, Alice, Alice, collectionIdAlice, 'child-nft-metadata'); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + childNftId, + src, + metadata, + license, + thumb, + ); + + const newOwnerNFT: NftIdTuple = [collectionIdAlice, parentNftId]; + + await sendNft(api, 'sent', Alice, collectionIdAlice, childNftId, newOwnerNFT); + + await removeNftResource(api, 'removed', Alice, collectionIdAlice, childNftId, resourceId); + }); + + it('deleting a resource by the collection owner', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftBob = await mintNft( + api, + Alice, + Bob, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'pending', + collectionIdAlice, + nftBob, + src, + metadata, + license, + thumb, + ); + + await removeNftResource(api, 'pending', Alice, collectionIdAlice, nftBob, resourceId); + await acceptResourceRemoval(api, Bob, collectionIdAlice, nftBob, resourceId); + }); + + it('deleting a resource in a nested NFT by the collection owner', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const parentNftId = await mintNft( + api, + Alice, + Bob, + collectionIdAlice, + 'parent-nft-metadata', + ); + const childNftId = await mintNft( + api, + Alice, + Bob, + collectionIdAlice, + 'child-nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'pending', + collectionIdAlice, + childNftId, + src, + metadata, + license, + thumb, + ); + + const newOwnerNFT: NftIdTuple = [collectionIdAlice, parentNftId]; + + await sendNft(api, 'sent', Bob, collectionIdAlice, childNftId, newOwnerNFT); + + await removeNftResource(api, 'pending', Alice, collectionIdAlice, childNftId, resourceId); + await acceptResourceRemoval(api, Bob, collectionIdAlice, childNftId, resourceId); + }); + + it('[negative]: can\'t delete a resource in a non-existing collection', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + + const tx = removeNftResource(api, 'removed', Alice, 0xFFFFFFFF, nftAlice, resourceId); + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); // FIXME: inappropriate error message (NoAvailableNftId) + }); + + it('[negative]: only collection owner can delete a resource', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + + const tx = removeNftResource(api, 'removed', Bob, collectionIdAlice, nftAlice, resourceId); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative]: cannot delete a resource that does not exist', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const tx = removeNftResource(api, 'removed', Alice, collectionIdAlice, nftAlice, 127); + await expectTxFailure(/rmrkCore\.ResourceDoesntExist/, tx); + }); + + it('[negative]: Cannot accept deleting resource without owner attempt do delete it', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftBob = await mintNft( + api, + Alice, + Bob, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'pending', + collectionIdAlice, + nftBob, + src, + metadata, + license, + thumb, + ); + + const tx = acceptResourceRemoval(api, Bob, collectionIdAlice, nftBob, resourceId); + await expectTxFailure(/rmrkCore\.ResourceNotPending/, tx); + }); + + it('[negative]: cannot confirm the deletion of a non-existing resource', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftBob = await mintNft( + api, + Alice, + Bob, + collectionIdAlice, + 'nft-metadata', + ); + + const tx = acceptResourceRemoval(api, Bob, collectionIdAlice, nftBob, 127); + await expectTxFailure(/rmrkCore\.ResourceDoesntExist/, tx); + }); + + it('[negative]: Non-owner user cannot confirm the deletion of resource', async () => { + const collectionIdAlice = await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ); + + const nftAlice = await mintNft( + api, + Alice, + Alice, + collectionIdAlice, + 'nft-metadata', + ); + + const resourceId = await addNftBasicResource( + api, + Alice, + 'added', + collectionIdAlice, + nftAlice, + src, + metadata, + license, + thumb, + ); + + const tx = acceptResourceRemoval(api, Bob, collectionIdAlice, nftAlice, resourceId); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/rmrk.test.ts b/tests/src/rmrk/rmrkIsolation.test.ts similarity index 99% rename from tests/src/rmrk/rmrk.test.ts rename to tests/src/rmrk/rmrkIsolation.test.ts index 024262fa1a..6ea368e20a 100644 --- a/tests/src/rmrk/rmrk.test.ts +++ b/tests/src/rmrk/rmrkIsolation.test.ts @@ -1,5 +1,4 @@ import {expect} from 'chai'; -import privateKey from '../substrate/privateKey'; import usingApi, {executeTransaction} from '../substrate/substrate-api'; import { createCollectionExpectSuccess, diff --git a/tests/src/rmrk/sendNft.test.ts b/tests/src/rmrk/sendNft.test.ts new file mode 100644 index 0000000000..178b1bccad --- /dev/null +++ b/tests/src/rmrk/sendNft.test.ts @@ -0,0 +1,252 @@ +import {expect} from 'chai'; +import {getApiConnection} from '../substrate/substrate-api'; +import {createCollection, mintNft, sendNft} from './util/tx'; +import {NftIdTuple} from './util/fetch'; +import {isNftChildOfAnother, expectTxFailure} from './util/helpers'; + +describe('integration test: send NFT', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const maxNftId = 0xFFFFFFFF; + + const alice = '//Alice'; + const bob = '//Bob'; + + const createTestCollection = async (issuerUri: string) => { + return await createCollection( + api, + issuerUri, + 'nft-collection-metadata', + null, + 'nft-collection', + ); + }; + + + it('send NFT to another user', async () => { + const originalOwnerUri = alice; + const newOwnerUri = bob; + + const collectionId = await createTestCollection(alice); + + const nftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'nft-metadata'); + + await sendNft(api, 'sent', originalOwnerUri, collectionId, nftId, newOwnerUri); + }); + + it('[negative] unable to send non-existing NFT', async () => { + const originalOwnerUri = alice; + const newOwnerUri = bob; + + const collectionId = 0; + const tx = sendNft(api, 'sent', originalOwnerUri, collectionId, maxNftId, newOwnerUri); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] unable to send NFT by a not-an-owner', async () => { + const originalOwnerUri = alice; + const newOwnerUri = bob; + + const collectionId = await createTestCollection(alice); + + const nftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'nft-metadata'); + + const tx = sendNft(api, 'sent', newOwnerUri, collectionId, nftId, newOwnerUri); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('send NFT to another NFT (same owner)', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', alice, collectionId, childNftId, newOwnerNFT); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.true; + }); + + it('[negative] send non-existing NFT to another NFT', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'parent-nft-metadata'); + const childNftId = maxNftId; + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + const tx = sendNft(api, 'sent', alice, collectionId, childNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.false; + }); + + it('send NFT to another NFT (by not-an-owner)', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const author = alice; + const attacker = bob; + + const parentNftId = await mintNft(api, author, originalOwnerUri, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, author, originalOwnerUri, collectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + const tx = sendNft(api, 'sent', attacker, collectionId, childNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.false; + }); + + it('[negative] send NFT to non-existing NFT', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const parentNftId = maxNftId; + const childNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + const tx = sendNft(api, 'sent', alice, collectionId, childNftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.false; + }); + + it('send NFT to another NFT owned by another user', async () => { + const ownerAlice = alice; + const ownerBob = bob; + + const aliceCollectionId = await createTestCollection(alice); + const bobCollectionId = await createTestCollection(bob); + + const parentNftId = await mintNft(api, alice, ownerAlice, aliceCollectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, bob, ownerBob, bobCollectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [aliceCollectionId, parentNftId]; + + await sendNft(api, 'pending', bob, bobCollectionId, childNftId, newOwnerNFT); + }); + + it('[negative] unable to send NFT to itself', async () => { + const nftOwner = alice; + const collectionId = await createTestCollection(alice); + + const nftId = await mintNft(api, alice, nftOwner, collectionId, 'ouroboros-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionId, nftId]; + + const tx = sendNft(api, 'sent', alice, collectionId, nftId, newOwnerNFT); + + await expectTxFailure(/rmrkCore\.CannotSendToDescendentOrSelf/, tx); + + const isChild = await isNftChildOfAnother(api, collectionId, nftId, newOwnerNFT); + expect(isChild).to.be.false; + }); + + it('[negative] unable to send NFT to child NFT', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'child-nft-metadata'); + + const newOwnerNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', alice, collectionId, childNftId, newOwnerNFT); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, newOwnerNFT); + expect(isChild).to.be.true; + + const descendentOwner: NftIdTuple = [collectionId, childNftId]; + const tx = sendNft(api, 'sent', alice, collectionId, parentNftId, descendentOwner); + + await expectTxFailure(/rmrkCore\.CannotSendToDescendentOrSelf/, tx); + const isOuroboros = await isNftChildOfAnother(api, collectionId, parentNftId, descendentOwner); + expect(isOuroboros).to.be.false; + }); + + it('[negative] unable to send NFT to descendent NFT', async () => { + const originalOwnerUri = alice; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'child-nft-metadata'); + const grandsonNftId = await mintNft(api, alice, originalOwnerUri, collectionId, 'grandson-nft-metadata'); + + const ownerParentNFT: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', alice, collectionId, childNftId, ownerParentNFT); + + const isChild = await isNftChildOfAnother(api, collectionId, childNftId, ownerParentNFT); + expect(isChild).to.be.true; + + const ownerChildNFT: NftIdTuple = [collectionId, childNftId]; + await sendNft(api, 'sent', alice, collectionId, grandsonNftId, ownerChildNFT); + + const isGrandson = await isNftChildOfAnother(api, collectionId, grandsonNftId, ownerChildNFT); + expect(isGrandson).to.be.true; + + const ownerGrandsonNFT: NftIdTuple = [collectionId, grandsonNftId]; + const tx = sendNft(api, 'sent', alice, collectionId, parentNftId, ownerGrandsonNFT); + + await expectTxFailure(/rmrkCore\.CannotSendToDescendentOrSelf/, tx); + const isOuroboros = await isNftChildOfAnother(api, collectionId, parentNftId, ownerGrandsonNFT); + expect(isOuroboros).to.be.false; + }); + + it('send nested NFT to another user', async () => { + const originalOwner = alice; + const newOwner = bob; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwner, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, alice, originalOwner, collectionId, 'child-nft-metadata'); + + const parentNftTuple: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', originalOwner, collectionId, childNftId, parentNftTuple); + + await sendNft(api, 'sent', originalOwner, collectionId, childNftId, newOwner); + }); + + it('[negative] send nested NFT to another user (by a not-root-owner)', async () => { + const originalOwner = alice; + const newOwner = bob; + + const collectionId = await createTestCollection(alice); + + const parentNftId = await mintNft(api, alice, originalOwner, collectionId, 'parent-nft-metadata'); + const childNftId = await mintNft(api, alice, originalOwner, collectionId, 'child-nft-metadata'); + + const parentNftTuple: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', originalOwner, collectionId, childNftId, parentNftTuple); + + const tx = sendNft(api, 'sent', newOwner, collectionId, childNftId, newOwner); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/setCollectionProperty.test.ts b/tests/src/rmrk/setCollectionProperty.test.ts new file mode 100644 index 0000000000..39bafa12fc --- /dev/null +++ b/tests/src/rmrk/setCollectionProperty.test.ts @@ -0,0 +1,67 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, setPropertyCollection} from './util/tx'; + +describe('integration test: set collection property', () => { + const Alice = '//Alice'; + const Bob = '//Bob'; + + let api: any; + before(async () => { + api = await getApiConnection(); + }); + + it('set collection property', async () => { + await createCollection( + api, + Alice, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + await setPropertyCollection(api, Alice, collectionId, 'test_key', '42'); + await setPropertyCollection(api, Alice, collectionId, 'test_key', '10'); + await setPropertyCollection( + api, + Alice, + collectionId, + 'second_test_key', + '111', + ); + }); + }); + + it('[negative] set non-existing collection property', async () => { + const tx = setPropertyCollection( + api, + Alice, + 9999, + 'test_key', + '42', + ); + await expectTxFailure(/rmrkCore\.CollectionUnknown/, tx); + }); + + it('[negative] set property not an owner NFT collection issuer', async () => { + await createCollection( + api, + Bob, + 'test-metadata', + null, + 'test-symbol', + ).then(async (collectionId) => { + const tx = setPropertyCollection( + api, + Alice, + collectionId, + 'test_key', + '42', + ); + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + }); + + after(() => { + api.disconnect(); + }); +}); diff --git a/tests/src/rmrk/setEquippableList.test.ts b/tests/src/rmrk/setEquippableList.test.ts new file mode 100644 index 0000000000..d36ce98a33 --- /dev/null +++ b/tests/src/rmrk/setEquippableList.test.ts @@ -0,0 +1,111 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, createBase, setEquippableList} from './util/tx'; + +describe("integration test: set slot's Equippable List", () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + it("set Base's slot Equippable List", async () => { + const collectionIds = [ + await createCollection( + api, + alice, + 'equiplist-collection-metadata', + null, + 'equiplist-0', + ), + await createCollection( + api, + alice, + 'equiplist-collection-metadata', + null, + 'equiplist-1', + ), + ]; + + const slotId = 202; + + const baseId = await createBase(api, alice, 'slotpartany-base-type', 'slotpartany', [ + { + 'SlotPart': { + id: slotId, + equippable: 'All', + z: 1, + src: 'some-fallback-slot-url', + }, + }, + ]); + + await setEquippableList(api, alice, baseId, slotId, 'All'); + await setEquippableList(api, alice, baseId, slotId, 'Empty'); + await setEquippableList(api, alice, baseId, slotId, {'Custom': collectionIds}); + }); + + it('[negative] unable to set equippable list of a slot of non-existing base', async () => { + const maxBaseId = 0xFFFFFFFF; + const slotId = 0; + + const tx = setEquippableList(api, alice, maxBaseId, slotId, 'All'); + await expectTxFailure(/rmrkEquip\.BaseDoesntExist/, tx); + }); + + it('[negative] unable to set equippable list by a not-an-owner', async () => { + const slotId = 42; + + const baseId = await createBase(api, alice, 'slotpartany-base-type', 'slotpartany', [ + { + 'SlotPart': { + id: slotId, + equippable: 'All', + z: 1, + src: 'some-fallback-slot-url', + }, + }, + ]); + + const tx = setEquippableList(api, bob, baseId, slotId, 'All'); + await expectTxFailure(/rmrkEquip\.PermissionError/, tx); + }); + + it('[negative] unable to set equippable list to a fixed part', async () => { + const fixedPartId = 42; + + const baseId = await createBase(api, alice, 'fixedpart-base-type', 'fixedpart', [ + { + 'FixedPart': { + id: fixedPartId, + z: 0, + src: 'fixed-part-url', + }, + }, + ]); + + const tx = setEquippableList(api, alice, baseId, fixedPartId, 'All'); + await expectTxFailure(/rmrkEquip\.NoEquippableOnFixedPart/, tx); + }); + + it('[negative] unable to set equippable list to non-existing slot', async () => { + const slotId = 777; + const maxSlotId = 0xFFFFFFFF; + + const baseId = await createBase(api, alice, 'slotpartany-base-type', 'slotpartany', [ + { + 'SlotPart': { + id: slotId, + equippable: 'All', + z: 1, + src: 'some-fallback-slot-url', + }, + }, + ]); + + const tx = setEquippableList(api, alice, baseId, maxSlotId, 'All'); + await expectTxFailure(/rmrkEquip\.PartDoesntExist/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/setNftProperty.test.ts b/tests/src/rmrk/setNftProperty.test.ts new file mode 100644 index 0000000000..b1c8c69b0e --- /dev/null +++ b/tests/src/rmrk/setNftProperty.test.ts @@ -0,0 +1,85 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {NftIdTuple} from './util/fetch'; +import {expectTxFailure} from './util/helpers'; +import {createCollection, mintNft, sendNft, setNftProperty} from './util/tx'; + +describe('integration test: set NFT property', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + const createTestCollection = async (issuerUri: string) => { + return await createCollection( + api, + issuerUri, + 'setprop-nft-collection-metadata', + null, + 'setprop', + ); + }; + + it('set NFT property', async () => { + const ownerAlice = alice; + + const collectionId = await createTestCollection(alice); + const nftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-nft'); + + await setNftProperty(api, alice, collectionId, nftId, 'test-key', 'test-key-value'); + await setNftProperty(api, alice, collectionId, nftId, 'test-key', 'updated-key-value'); + await setNftProperty(api, alice, collectionId, nftId, 'second-test-key', 'second-test-key-value'); + }); + + it('[negative] unable to set a property of non-existing NFT', async () => { + const collectionId = 0; + const maxNftId = 0xFFFFFFFF; + + const tx = setNftProperty(api, alice, collectionId, maxNftId, 'test-key', 'test-value'); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + it('[negative] unable to set a property by not-an-owner', async () => { + const ownerAlice = alice; + + const collectionId = await createTestCollection(alice); + const nftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-nft'); + + const tx = setNftProperty(api, bob, collectionId, nftId, 'test-key', 'test-key-value'); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('set a property to nested NFT', async () => { + const ownerAlice = alice; + + const collectionId = await createTestCollection(alice); + const parentNftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-parent-nft'); + const childNftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-child-nft'); + + const ownerNft: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', ownerAlice, collectionId, childNftId, ownerNft); + + await setNftProperty(api, alice, collectionId, childNftId, 'test-key', 'test-key-value'); + }); + + it('[negative] set a property to nested NFT (by not-root-owner)', async () => { + const ownerAlice = alice; + + const collectionId = await createTestCollection(alice); + const parentNftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-parent-nft'); + const childNftId = await mintNft(api, alice, ownerAlice, collectionId, 'prop-child-nft'); + + const ownerNft: NftIdTuple = [collectionId, parentNftId]; + + await sendNft(api, 'sent', ownerAlice, collectionId, childNftId, ownerNft); + + const tx = setNftProperty(api, bob, collectionId, childNftId, 'test-key', 'test-key-value'); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/setResourcePriorities.test.ts b/tests/src/rmrk/setResourcePriorities.test.ts new file mode 100644 index 0000000000..6f7f1503c7 --- /dev/null +++ b/tests/src/rmrk/setResourcePriorities.test.ts @@ -0,0 +1,55 @@ +import {getApiConnection} from '../substrate/substrate-api'; +import {expectTxFailure} from './util/helpers'; +import {mintNft, createCollection, setResourcePriorities} from './util/tx'; + +describe('integration test: set NFT resource priorities', () => { + let api: any; + before(async () => { api = await getApiConnection(); }); + + const alice = '//Alice'; + const bob = '//Bob'; + + const createTestCollection = (issuerUri: string) => { + return createCollection( + api, + issuerUri, + 'resprio-collection-metadata', + null, + 'resprio', + ); + }; + + it('set NFT resource priorities', async () => { + const owner = alice; + + const collectionId = await createTestCollection(alice); + const nftId = await mintNft(api, alice, owner, collectionId, 'resprio-nft-metadata'); + + await setResourcePriorities(api, alice, collectionId, nftId, [10, 42]); + }); + + it('[negative] set NFT resource priorities by a not-an-owner', async () => { + const owner = alice; + const attacker = bob; + + const collectionId = await createTestCollection(alice); + const nftId = await mintNft(api, alice, owner, collectionId, 'resprio-nft-metadata'); + + const tx = setResourcePriorities(api, attacker, collectionId, nftId, [10, 42]); + + await expectTxFailure(/rmrkCore\.NoPermission/, tx); + }); + + it('[negative] set NFT resource priorities to non-existing NFT', async () => { + const owner = alice; + + const collectionId = 0; + const maxNftId = 0xFFFFFFFF; + + const tx = setResourcePriorities(api, alice, collectionId, maxNftId, [10, 42]); + + await expectTxFailure(/rmrkCore\.NoAvailableNftId/, tx); + }); + + after(() => { api.disconnect(); }); +}); diff --git a/tests/src/rmrk/util/fetch.ts b/tests/src/rmrk/util/fetch.ts new file mode 100644 index 0000000000..8122a669b9 --- /dev/null +++ b/tests/src/rmrk/util/fetch.ts @@ -0,0 +1,130 @@ +import {ApiPromise} from '@polkadot/api'; +import type {Option, Bytes} from '@polkadot/types-codec'; +import type { + RmrkTraitsCollectionCollectionInfo as Collection, + RmrkTraitsNftNftInfo as Nft, + RmrkTraitsResourceResourceInfo as Resource, + RmrkTraitsBaseBaseInfo as Base, + RmrkTraitsPartPartType as PartType, + RmrkTraitsNftNftChild as NftChild, + RmrkTraitsTheme as Theme, + RmrkTraitsPropertyPropertyInfo as Property, +} from '../../interfaces/default/types'; // '@polkadot/types/lookup'; +import '../../interfaces/augment-api'; +import '../../interfaces/augment-api-query'; +import privateKey from '../../substrate/privateKey'; + +export type NftIdTuple = [number, number]; + +export async function getCollectionsCount(api: ApiPromise): Promise { + return (await api.rpc.rmrk.lastCollectionIdx()).toNumber(); +} + +export async function getCollection(api: ApiPromise, id: number): Promise> { + return api.rpc.rmrk.collectionById(id); +} + +export async function getOwnedNfts( + api: ApiPromise, + ownerUri: string, + collectionId: number, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const owner = privateKey(ownerUri, Number(ss58Format)); + + return (await api.rpc.rmrk.accountTokens(owner.address, collectionId)) + .map((value) => value.toNumber()); +} + +export async function getNft(api: ApiPromise, collectionId: number, nftId: number): Promise> { + return api.rpc.rmrk.nftById(collectionId, nftId); +} + +export async function getCollectionProperties(api: ApiPromise, collectionId: number): Promise { + return (await api.rpc.rmrk.collectionProperties(collectionId)).toArray(); +} + +export async function getNftProperties(api: ApiPromise, collectionId: number, nftId: number): Promise { + return (await api.rpc.rmrk.nftProperties(collectionId, nftId)).toArray(); +} + +export async function getChildren( + api: ApiPromise, + collectionId: number, + nftId: number, +): Promise { + return (await api.rpc.rmrk.nftChildren(collectionId, nftId)).toArray(); +} + +export async function getBase(api: ApiPromise, baseId: number): Promise> { + return api.rpc.rmrk.base(baseId); +} + +export async function getParts(api: ApiPromise, baseId: number): Promise { + return (await api.rpc.rmrk.baseParts(baseId)).toArray(); +} + +export async function getEquippableList( + api: ApiPromise, + baseId: number, + slotId: number, +): Promise<'All' | 'Empty' | { 'Custom': number[] } | null> { + const parts = await getParts(api, baseId); + + const part = parts.find((part) => { + if (part.isSlotPart) { + return part.asSlotPart.id.toNumber() === slotId; + } else { + return false; + } + }); + + if (part) { + const slot = part.asSlotPart; + if (slot.equippable.isCustom) { + return { + 'Custom': slot.equippable.asCustom + .toArray() + .map((collectionId) => collectionId.toNumber()), + }; + } else if (slot.equippable.isAll) { + return 'All'; + } else { + return 'Empty'; + } + } else { + return null; + } +} + +export async function getResourcePriority( + api: ApiPromise, + collectionId: number, + nftId: number, + resourceId: number, +): Promise { + return (await api.rpc.rmrk.nftResourcePriority(collectionId, nftId, resourceId)) + .unwrap().toNumber(); +} + +export async function getThemeNames(api: ApiPromise, baseId: number): Promise { + return (await api.rpc.rmrk.themeNames(baseId)) + .map((name) => name.toUtf8()); +} + +export async function getTheme( + api: ApiPromise, + baseId: number, + themeName: string, + keys: string[] | null = null, +): Promise> { + return api.rpc.rmrk.themes(baseId, themeName, keys); +} + +export async function getResources( + api: ApiPromise, + collectionId: number, + nftId: number, +): Promise { + return (await api.rpc.rmrk.nftResources(collectionId, nftId)).toArray(); +} diff --git a/tests/src/rmrk/util/helpers.ts b/tests/src/rmrk/util/helpers.ts new file mode 100644 index 0000000000..c43fccb5a9 --- /dev/null +++ b/tests/src/rmrk/util/helpers.ts @@ -0,0 +1,207 @@ +import {ApiPromise} from '@polkadot/api'; +import { + RmrkTraitsNftAccountIdOrCollectionNftTuple as NftOwner, + RmrkTraitsPropertyPropertyInfo as Property, + RmrkTraitsResourceResourceInfo as ResourceInfo, +} from '@polkadot/types/lookup'; +import type {EventRecord} from '@polkadot/types/interfaces'; +import type {GenericEventData} from '@polkadot/types'; +import privateKey from '../../substrate/privateKey'; +import {NftIdTuple, getChildren, getOwnedNfts, getCollectionProperties, getNftProperties, getResources} from './fetch'; +import chaiAsPromised from 'chai-as-promised'; +import chai from 'chai'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +interface TxResult { + success: boolean; + successData: T | null; +} + +export function makeNftOwner(api: ApiPromise, owner: string | NftIdTuple): NftOwner { + const isNftSending = (typeof owner !== 'string'); + + if (isNftSending) { + return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', { + 'CollectionAndNftTuple': owner, + }); + } else { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + return api.createType('RmrkTraitsNftAccountIdOrCollectionNftTuple', { + AccountId: privateKey(owner, Number(ss58Format)).address, + }); + } +} + +export async function isNftOwnedBy( + api: ApiPromise, + owner: string | NftIdTuple, + collectionId: number, + nftId: number, +): Promise { + if (typeof owner === 'string') { + return (await getOwnedNfts(api, owner, collectionId)) + .find(ownedNftId => { + return ownedNftId === nftId; + }) !== undefined; + } else { + return (await getChildren(api, owner[0], owner[1])) + .find(child => { + return collectionId === child.collectionId.toNumber() + && nftId === child.nftId.toNumber(); + }) !== undefined; + } +} + +export function isPropertyExists( + key: string, + value: string, + props: Property[], +): boolean { + let isPropFound = false; + for (let i = 0; i < props.length && !isPropFound; i++) { + const fetchedProp = props[i]; + + isPropFound = fetchedProp.key.eq(key) + && fetchedProp.value.eq(value); + } + + return isPropFound; +} + +export async function isCollectionPropertyExists( + api: ApiPromise, + collectionId: number, + key: string, + value: string, +): Promise { + const fetchedProps = await getCollectionProperties(api, collectionId); + return isPropertyExists(key, value, fetchedProps); +} + +export async function isNftPropertyExists( + api: ApiPromise, + collectionId: number, + nftId: number, + key: string, + value: string, +): Promise { + const fetchedProps = await getNftProperties(api, collectionId, nftId); + return isPropertyExists(key, value, fetchedProps); +} + +export async function isNftChildOfAnother( + api: ApiPromise, + collectionId: number, + nftId: number, + parentNft: NftIdTuple, +): Promise { + return (await getChildren(api, parentNft[0], parentNft[1])) + .find((childNft) => { + return childNft.collectionId.toNumber() === collectionId + && childNft.nftId.toNumber() === nftId; + }) !== undefined; +} + +export function isTxResultSuccess(events: EventRecord[]): boolean { + let success = false; + + events.forEach(({event: {data, method, section}}) => { + if (method == 'ExtrinsicSuccess') { + success = true; + } + }); + + return success; +} + +export async function expectTxFailure(expectedError: RegExp, promise: Promise) { + await expect(promise).to.be.rejectedWith(expectedError); +} + +export function extractTxResult( + events: EventRecord[], + expectSection: string, + expectMethod: string, + extractAction: (data: any) => T, +): TxResult { + let success = false; + let successData = null; + events.forEach(({event: {data, method, section}}) => { + if (method == 'ExtrinsicSuccess') { + success = true; + } else if ((expectSection == section) && (expectMethod == method)) { + successData = extractAction(data); + } + }); + const result: TxResult = { + success, + successData, + }; + return result; +} + +export function extractRmrkCoreTxResult( + events: EventRecord[], + expectMethod: string, + extractAction: (data: GenericEventData) => T, +): TxResult { + return extractTxResult(events, 'rmrkCore', expectMethod, extractAction); +} + +export function extractRmrkEquipTxResult( + events: EventRecord[], + expectMethod: string, + extractAction: (data: GenericEventData) => T, +): TxResult { + return extractTxResult(events, 'rmrkEquip', expectMethod, extractAction); +} + +export async function findResourceById( + api: ApiPromise, + collectionId: number, + nftId: number, + resourceId: number, +): Promise { + const resources = await getResources(api, collectionId, nftId); + + let resource = null; + + for (let i = 0; i < resources.length; i++) { + const res = resources[i]; + + if (res.id.eq(resourceId)) { + resource = res; + break; + } + } + + return resource!; +} + +export async function getResourceById( + api: ApiPromise, + collectionId: number, + nftId: number, + resourceId: number, +): Promise { + const resource = await findResourceById( + api, + collectionId, + nftId, + resourceId, + ); + + expect(resource !== null, 'Error: resource was not found').to.be.true; + + return resource!; +} + +export function checkResourceStatus( + resource: ResourceInfo, + expectedStatus: 'pending' | 'added', +) { + expect(resource.pending.isTrue, `Error: added resource should be ${expectedStatus}`) + .to.be.equal(expectedStatus === 'pending'); +} diff --git a/tests/src/rmrk/util/tx.ts b/tests/src/rmrk/util/tx.ts new file mode 100644 index 0000000000..fe18443a10 --- /dev/null +++ b/tests/src/rmrk/util/tx.ts @@ -0,0 +1,1132 @@ +import {ApiPromise} from '@polkadot/api'; +import {Bytes, Option, u32, Vec} from '@polkadot/types-codec'; +import { + RmrkTraitsNftAccountIdOrCollectionNftTuple as NftOwner, RmrkTraitsPartEquippableList as EquippableList, + RmrkTraitsPartPartType as PartType, RmrkTraitsResourceBasicResource as BasicResource, + RmrkTraitsResourceComposableResource as ComposableResource, RmrkTraitsResourceResourceInfo as ResourceInfo, RmrkTraitsResourceSlotResource as SlotResource, RmrkTraitsTheme as Theme, +} from '@polkadot/types/lookup'; +import {IKeyringPair} from '@polkadot/types/types'; +import chai from 'chai'; +import chaiAsPromised from 'chai-as-promised'; +import '../../interfaces/augment-api'; +import privateKey from '../../substrate/privateKey'; +import {executeTransaction} from '../../substrate/substrate-api'; +import { + getBase, + getCollection, + getCollectionsCount, + getEquippableList, + getNft, + getParts, + getResources, + getResourcePriority, + getTheme, + NftIdTuple, +} from './fetch'; +import { + extractRmrkCoreTxResult, + extractRmrkEquipTxResult, isCollectionPropertyExists, isNftOwnedBy, isNftPropertyExists, isTxResultSuccess, makeNftOwner, + findResourceById, getResourceById, checkResourceStatus, +} from './helpers'; + +chai.use(chaiAsPromised); +const expect = chai.expect; + +export async function createCollection( + api: ApiPromise, + issuerUri: string, + metadata: string, + max: number | null, + symbol: string, +): Promise { + let collectionId = 0; + + const oldCollectionCount = await getCollectionsCount(api); + const maxOptional = max ? max.toString() : null; + const ss58Format = (api.registry.getChainProperties())!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkCore.createCollection(metadata, maxOptional, symbol); + const events = await executeTransaction(api, issuer, tx); + + const collectionResult = extractRmrkCoreTxResult(events, 'CollectionCreated', (data) => { + return parseInt(data[1].toString(), 10); + }); + expect(collectionResult.success, 'Error: unable to create a collection').to.be.true; + + collectionId = collectionResult.successData!; + + const newCollectionCount = await getCollectionsCount(api); + const collectionOption = await getCollection(api, collectionId); + + expect(newCollectionCount).to.be.equal(oldCollectionCount + 1, 'Error: NFT collection count should increase'); + expect(collectionOption.isSome, 'Error: unable to fetch created NFT collection').to.be.true; + + const collection = collectionOption.unwrap(); + + expect(collection.metadata.toUtf8()).to.be.equal(metadata, 'Error: Invalid NFT collection metadata'); + expect(collection.max.isSome).to.be.equal(max !== null, 'Error: Invalid NFT collection max'); + + if (collection.max.isSome) { + expect(collection.max.unwrap().toNumber()).to.be.equal(max, 'Error: Invalid NFT collection max'); + } + expect(collection.symbol.toUtf8()).to.be.equal(symbol, "Error: Invalid NFT collection's symbol"); + expect(collection.nftsCount.toNumber()).to.be.equal(0, "Error: NFT collection shoudn't have any tokens"); + expect(collection.issuer.toString()).to.be.equal(issuer.address, 'Error: Invalid NFT collection issuer'); + + return collectionId; +} + +export async function changeIssuer( + api: ApiPromise, + issuerUri: string, + collectionId: number, + newIssuer: string, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const alice = privateKey(issuerUri, Number(ss58Format)); + const bob = privateKey(newIssuer, Number(ss58Format)); + + // This is only needed when RMRK uses `uniques` pallet by Parity + // let tx = api.tx.uniques.setAcceptOwnership( + // api.createType('Option', collectionId) + // ); + // let events = await executeTransaction(api, bob, tx); + // expect(isTxResultSuccess(events), 'Error: Unable to accept ownership').to.be.true; + + const tx = api.tx.rmrkCore.changeCollectionIssuer(collectionId, bob.address); + const events = await executeTransaction(api, alice, tx); + const changeIssuerResult = extractRmrkCoreTxResult(events, 'IssuerChanged', (data) => { + return parseInt(data[2].toString(), 10); + }); + expect(changeIssuerResult.success, 'Error: Unable to change NFT collection issuer').to.be.true; + expect(changeIssuerResult.successData!, 'Error: Invalid collection id after changing the issuer') + .to.be.eq(collectionId); + + await getCollection(api, collectionId).then((collectionOption) => { + const collection = collectionOption.unwrap(); + expect(collection.issuer.toString()) + .to.be.deep.eq(bob.address, 'Error: Invalid NFT collection issuer'); + }); +} + +export async function deleteCollection( + api: ApiPromise, + issuerUri: string, + collectionId: string, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkCore.destroyCollection(collectionId); + const events = await executeTransaction(api, issuer, tx); + + const collectionTxResult = extractRmrkCoreTxResult( + events, + 'CollectionDestroy', + (data) => { + return parseInt(data[1].toString(), 10); + }, + ); + expect(collectionTxResult.success, 'Error: Unable to delete NFT collection').to.be.true; + + const collection = await getCollection( + api, + parseInt(collectionId, 10), + ); + expect(collection.isEmpty, 'Error: NFT collection should be deleted').to.be.true; + + return 0; +} + +export async function negativeDeleteCollection( + api: ApiPromise, + issuerUri: string, + collectionId: string, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkCore.destroyCollection(collectionId); + await expect(executeTransaction(api, issuer, tx)).to.be.rejected; + + return 0; +} + +export async function setNftProperty( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, + key: string, + value: string, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const nftIdOpt = api.createType('Option', nftId); + const tx = api.tx.rmrkCore.setProperty( + collectionId, + nftIdOpt, + key, + value, + ); + const events = await executeTransaction(api, issuer, tx); + + const propResult = extractRmrkCoreTxResult(events, 'PropertySet', (data) => { + return { + collectionId: parseInt(data[0].toString(), 10), + nftId: data[1] as Option, + key: data[2] as Bytes, + value: data[3] as Bytes, + }; + }); + + expect(propResult.success, 'Error: Unable to set NFT property').to.be.true; + const eventData = propResult.successData!; + const eventDescription = 'from set NFT property event'; + + expect(eventData.collectionId, 'Error: Invalid collection ID ' + eventDescription) + .to.be.equal(collectionId); + + expect(eventData.nftId.eq(nftIdOpt), 'Error: Invalid NFT ID ' + eventDescription) + .to.be.true; + + expect(eventData.key.eq(key), 'Error: Invalid property key ' + eventDescription) + .to.be.true; + + expect(eventData.value.eq(value), 'Error: Invalid property value ' + eventDescription) + .to.be.true; + + expect( + await isNftPropertyExists(api, collectionId, nftId, key, value), + 'Error: NFT property is not found', + ).to.be.true; +} + +export async function mintNft( + api: ApiPromise, + issuerUri: string, + ownerUri: string | null, + collectionId: number, + metadata: string, + recipientUri: string | null = null, + royalty: number | null = null, + transferable = true, + resources: {basic?: any, composable?: any, slot?: any}[] | null = null, +): Promise { + let nftId = 0; + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const owner = ownerUri ? privateKey(ownerUri, Number(ss58Format)).address : null; + const recipient = recipientUri ? privateKey(recipientUri, Number(ss58Format)).address : null; + const royaltyOptional = royalty ? royalty.toString() : null; + + const actualOwnerUri = ownerUri ? ownerUri : issuerUri; + const actualOwnerAddress = ownerUri ? owner : issuer.address; + + const collectionOpt = await getCollection(api, collectionId); + + const tx = api.tx.rmrkCore.mintNft( + owner, + collectionId, + recipient, + royaltyOptional, + metadata, + transferable, + resources, + ); + + const events = await executeTransaction(api, issuer, tx); + const nftResult = extractRmrkCoreTxResult(events, 'NftMinted', (data) => { + return parseInt(data[2].toString(), 10); + }); + + expect(nftResult.success, 'Error: Unable to mint NFT').to.be.true; + + const newCollectionNftsCount = (await getCollection(api, collectionId)) + .unwrap() + .nftsCount + .toNumber(); + + const oldCollectionNftsCount = collectionOpt + .unwrap() + .nftsCount.toNumber(); + + expect(newCollectionNftsCount, 'Error: NFTs count should increase') + .to.be.equal(oldCollectionNftsCount + 1); + + nftId = nftResult.successData!; + + const nftOption = await getNft(api, collectionId, nftId); + + expect(nftOption.isSome, 'Error: Unable to fetch created NFT').to.be.true; + + const nft = nftOption.unwrap(); + + expect(nft.owner.isAccountId, 'Error: NFT owner should be some user').to.be.true; + expect(nft.owner.asAccountId.toString()).to.be.equal(actualOwnerAddress, 'Error: Invalid NFT owner'); + + const isOwnedInUniques = await isNftOwnedBy(api, actualOwnerUri, collectionId, nftId); + expect(isOwnedInUniques, `Error: created NFT is not actually owned by ${ownerUri}`) + .to.be.true; + + if (recipient === null && royalty === null) { + expect(nft.royalty.isNone, 'Error: Invalid NFT recipient') + .to.be.true; + } else { + expect(nft.royalty.isSome, 'Error: NFT royalty not found') + .to.be.true; + + const nftRoyalty = nft.royalty.unwrap(); + expect(nftRoyalty.recipient.eq(recipient), 'Error: Invalid NFT recipient') + .to.be.true; + + expect(nftRoyalty.amount.eq(royalty), 'Error: Invalid NFT royalty') + .to.be.true; + } + + expect(nft.metadata.toUtf8()).to.be.equal(metadata, 'Error: Invalid NFT metadata'); + + const nftResources = await getResources(api, collectionId, nftId); + if (resources == null) { + expect(nftResources, 'Error: Invalid NFT resources').to.be.empty; + } else { + expect(nftResources.length).to.be.equal(resources.length); + + for (let i = 0; i < resources.length; i++) { + let successFindingResource = false; + const resource = resources[i]; + // try to find the matching resource from the query + for (let j = 0; j < nftResources.length && !successFindingResource; j++) { + const nftResourceData = nftResources[j].toHuman(); + expect( + Object.prototype.hasOwnProperty.call(nftResourceData, 'resource'), + `Error: Corrupted resource data on resource #${i}`, + ).to.be.true; + const nftResource = nftResourceData.resource!; + type NftResourceKey = keyof typeof nftResource; + + let typedResource = null; + let typedNftResource = null; + + if (resource.basic && Object.prototype.hasOwnProperty.call(nftResource, 'Basic')) { + typedResource = resource.basic!; + typedNftResource = nftResource['Basic' as NftResourceKey]!; + } else if (resource.composable && Object.prototype.hasOwnProperty.call(nftResource, 'Composable')) { + typedResource = resource.composable!; + typedNftResource = nftResource['Composable' as NftResourceKey]! as any; + if (typedResource.parts != undefined && typedResource.parts.toString() != typedNftResource.parts.toString() + || typedResource.base != typedNftResource.base && typedResource.base != undefined) { + continue; + } + } else if (resource.slot && Object.prototype.hasOwnProperty.call(nftResource, 'Slot')) { + typedResource = resource.slot!; + typedNftResource = nftResource['Slot' as NftResourceKey]! as any; + if (typedResource.slot != typedNftResource.slot && typedResource.slot != undefined + || typedResource.base != typedNftResource.base && typedResource.base != undefined) { + continue; + } + } else { + continue; + } + + if (typedResource.src != typedNftResource.src + || typedResource.metadata != typedNftResource.metadata + || typedResource.thumb != typedNftResource.thumb + || typedResource.license != typedNftResource.license + ) { + continue; + } + + // do final checks since this is now established to be the resource we seek + expect(nftResourceData.pending, `Error: Resource #${i} is pending`).to.be.false; + expect(nftResourceData.pendingRemoval, `Error: Resource #${i} is pending removal`).to.be.false; + + // remove the matching resource from the resources we check + nftResources.splice(j, 1); + successFindingResource = true; + } + + expect(successFindingResource, `Error: Couldn't find resource #${i}'s counterpart among the returned`).to.be.true; + } + } + + return nftId; +} + +export async function sendNft( + api: ApiPromise, + expectedStatus: 'pending' | 'sent', + originalOwnerUri: string, + collectionId: number, + nftId: number, + newOwner: string | NftIdTuple, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const originalOwner = privateKey(originalOwnerUri, Number(ss58Format)); + const newOwnerObj = makeNftOwner(api, newOwner); + + const nftBeforeSendingOpt = await getNft(api, collectionId, nftId); + + const tx = api.tx.rmrkCore.send(collectionId, nftId, newOwnerObj); + const events = await executeTransaction(api, originalOwner, tx); + + const sendResult = extractRmrkCoreTxResult(events, 'NFTSent', (data) => { + return { + dstOwner: data[1] as NftOwner, + collectionId: parseInt(data[2].toString(), 10), + nftId: parseInt(data[3].toString(), 10), + }; + }); + + expect(sendResult.success, 'Error: Unable to send NFT').to.be.true; + const sendData = sendResult.successData!; + + expect(sendData.dstOwner.eq(newOwnerObj), 'Error: Invalid target user (from event data)') + .to.be.true; + + expect(sendData.collectionId) + .to.be.equal(collectionId, 'Error: Invalid collection ID (from event data)'); + + expect(sendData.nftId).to.be.equal(nftId, 'Error: Invalid NFT ID (from event data)'); + + expect(nftBeforeSendingOpt.isSome, 'Error: Unable to fetch NFT before sending').to.be.true; + + const nftBeforeSending = nftBeforeSendingOpt.unwrap(); + + const nftAfterSendingOpt = await getNft(api, collectionId, nftId); + + expect(nftAfterSendingOpt.isSome, 'Error: Unable to fetch NFT after sending').to.be.true; + + const nftAfterSending = nftAfterSendingOpt.unwrap(); + + const isOwnedByNewOwner = await isNftOwnedBy(api, newOwner, collectionId, nftId); + const isPending = expectedStatus === 'pending'; + + expect( + isOwnedByNewOwner, + `Error: The NFT should be owned by ${newOwner.toString()}`, + ).to.be.true; + + expect(nftAfterSending.royalty.eq(nftBeforeSending.royalty), 'Error: Invalid NFT royalty after sending') + .to.be.true; + + expect(nftAfterSending.metadata.eq(nftBeforeSending.metadata), 'Error: Invalid NFT metadata after sending') + .to.be.true; + + expect(nftAfterSending.equipped.eq(nftBeforeSending.equipped), 'Error: Invalid NFT equipped status after sending') + .to.be.true; + + expect(nftAfterSending.pending.eq(isPending), 'Error: Invalid NFT pending state') + .to.be.true; +} + +export async function acceptNft( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, + newOwner: string | [number, number], +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const newOwnerObj = makeNftOwner(api, newOwner); + + const nftBeforeOpt = await getNft(api, collectionId, nftId); + + const tx = api.tx.rmrkCore.acceptNft(collectionId, nftId, newOwnerObj); + const events = await executeTransaction(api, issuer, tx); + + const acceptResult = extractRmrkCoreTxResult(events, 'NFTAccepted', (data) => { + return { + recipient: data[1] as NftOwner, + collectionId: parseInt(data[2].toString(), 10), + nftId: parseInt(data[3].toString(), 10), + }; + }); + + expect(acceptResult.success, 'Error: Unable to accept NFT').to.be.true; + const acceptData = acceptResult.successData!; + + expect(acceptData.recipient.eq(newOwnerObj), 'Error: Invalid NFT recipient (from event data)') + .to.be.true; + + expect(acceptData.collectionId) + .to.be.equal(collectionId, 'Error: Invalid collection ID (from event data)'); + + expect(acceptData.nftId) + .to.be.equal(nftId, 'Error: Invalid NFT ID (from event data)'); + + const nftBefore = nftBeforeOpt.unwrap(); + + const isPendingBeforeAccept = nftBefore.pending.isTrue; + + const nftAfter = (await getNft(api, collectionId, nftId)).unwrap(); + const isPendingAfterAccept = nftAfter.pending.isTrue; + + expect(isPendingBeforeAccept, 'Error: NFT should be pending to be accepted') + .to.be.true; + + expect(isPendingAfterAccept, 'Error: NFT should NOT be pending after accept') + .to.be.false; + + const isOwnedInUniques = await isNftOwnedBy(api, newOwner, collectionId, nftId); + expect(isOwnedInUniques, `Error: created NFT is not actually owned by ${newOwner.toString()}`) + .to.be.true; +} + +export async function rejectNft( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const nftBeforeOpt = await getNft(api, collectionId, nftId); + + const tx = api.tx.rmrkCore.rejectNft(collectionId, nftId); + const events = await executeTransaction(api, issuer, tx); + const rejectResult = extractRmrkCoreTxResult(events, 'NFTRejected', (data) => { + return { + collectionId: parseInt(data[1].toString(), 10), + nftId: parseInt(data[2].toString(), 10), + }; + }); + + const rejectData = rejectResult.successData!; + + expect(rejectData.collectionId) + .to.be.equal(collectionId, 'Error: Invalid collection ID (from event data)'); + + expect(rejectData.nftId) + .to.be.equal(nftId, 'Error: Invalid NFT ID (from event data)'); + + const nftBefore = nftBeforeOpt.unwrap(); + + const isPendingBeforeReject = nftBefore.pending.isTrue; + + const nftAfter = await getNft(api, collectionId, nftId); + + expect(isPendingBeforeReject, 'Error: NFT should be pending to be rejected') + .to.be.true; + + expect(nftAfter.isNone, 'Error: NFT should be burned after reject') + .to.be.true; +} + +export async function createBase( + api: ApiPromise, + issuerUri: string, + baseType: string, + symbol: string, + parts: object[], +): Promise { + let baseId = 0; + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const partTypes = api.createType('Vec', parts) as Vec; + + const tx = api.tx.rmrkEquip.createBase(baseType, symbol, partTypes); + const events = await executeTransaction(api, issuer, tx); + + const baseResult = extractRmrkEquipTxResult(events, 'BaseCreated', (data) => { + return parseInt(data[1].toString(), 10); + }); + + expect(baseResult.success, 'Error: Unable to create Base') + .to.be.true; + + baseId = baseResult.successData!; + const baseOptional = await getBase(api, baseId); + + expect(baseOptional.isSome, 'Error: Unable to fetch created Base') + .to.be.true; + + const base = baseOptional.unwrap(); + const baseParts = await getParts(api, baseId); + + expect(base.issuer.toString()).to.be.equal(issuer.address, 'Error: Invalid Base issuer'); + expect(base.baseType.toUtf8()).to.be.equal(baseType, 'Error: Invalid Base type'); + expect(base.symbol.toUtf8()).to.be.equal(symbol, 'Error: Invalid Base symbol'); + expect(partTypes.eq(baseParts), 'Error: Received invalid base parts').to.be.true; + + return baseId; +} + +export async function setResourcePriorities( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, + priorities: number[], +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const prioritiesVec = api.createType('Vec', priorities); + const tx = api.tx.rmrkCore.setPriority(collectionId, nftId, prioritiesVec); + const events = await executeTransaction(api, issuer, tx); + + const prioResult = extractRmrkCoreTxResult(events, 'PrioritySet', (data) => { + return { + collectionId: parseInt(data[0].toString(), 10), + nftId: parseInt(data[1].toString(), 10), + }; + }); + + expect(prioResult.success, 'Error: Unable to set resource priorities').to.be.true; + const eventData = prioResult.successData!; + + expect(eventData.collectionId) + .to.be.equal(collectionId, 'Error: Invalid collection ID (set priorities event data)'); + + expect(eventData.nftId).to.be.equal(nftId, 'Error: Invalid NFT ID (set priorities event data'); + + for (let i = 0; i < priorities.length; i++) { + const resourceId = priorities[i]; + + const fetchedPrio = await getResourcePriority(api, collectionId, nftId, resourceId); + expect(fetchedPrio).to.be.equal(i, 'Error: Invalid priorities are set'); + } + +} + +export async function setEquippableList( + api: ApiPromise, + issuerUri: string, + baseId: number, + slotId: number, + equippableList: 'All' | 'Empty' | { 'Custom': number[] }, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const equippable = api.createType('RmrkTraitsPartEquippableList', equippableList) as EquippableList; + + const tx = api.tx.rmrkEquip.equippable(baseId, slotId, equippable); + const events = await executeTransaction(api, issuer, tx); + + const equipListResult = extractRmrkEquipTxResult(events, 'EquippablesUpdated', (data) => { + return { + baseId: parseInt(data[0].toString(), 10), + slotId: parseInt(data[1].toString(), 10), + }; + }); + + expect(equipListResult.success, 'Error: unable to update equippable list').to.be.true; + const updateEvent = equipListResult.successData!; + + expect(updateEvent.baseId) + .to.be.equal(baseId, 'Error: invalid base ID from update equippable event'); + + expect(updateEvent.slotId) + .to.be.equal(slotId, 'Error: invalid base ID from update equippable event'); + + const fetchedEquippableList = await getEquippableList(api, baseId, slotId); + + expect(fetchedEquippableList, 'Error: unable to fetch equippable list').to.be.not.null; + if (fetchedEquippableList) { + expect(fetchedEquippableList) + .to.be.deep.equal(equippableList, 'Error: invalid equippable list was set'); + } +} + +export async function addTheme( + api: ApiPromise, + issuerUri: string, + baseId: number, + themeObj: object, + filterKeys: string[] | null = null, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const theme = api.createType('RmrkTraitsTheme', themeObj) as Theme; + + const tx = api.tx.rmrkEquip.themeAdd(baseId, theme); + const events = await executeTransaction(api, issuer, tx); + + expect(isTxResultSuccess(events), 'Error: Unable to add Theme').to.be.true; + + const fetchedThemeOpt = await getTheme(api, baseId, theme.name.toUtf8(), null); + + expect(fetchedThemeOpt.isSome, 'Error: Unable to fetch theme').to.be.true; + + const fetchedTheme = fetchedThemeOpt.unwrap(); + + expect(theme.name.eq(fetchedTheme.name), 'Error: Invalid theme name').to.be.true; + + for (let i = 0; i < theme.properties.length; i++) { + const property = theme.properties[i]; + const propertyKey = property.key.toUtf8(); + + const propertyFoundCount = fetchedTheme.properties.filter((fetchedProp) => property.key.eq(fetchedProp.key)).length; + + expect(propertyFoundCount > 1, `Error: Too many properties with key ${propertyKey} found`) + .to.be.false; + + if (filterKeys) { + const isFiltered = fetchedTheme.properties.find((fetchedProp) => fetchedProp.key.eq(property.key)) === undefined; + + if (isFiltered) { + expect(propertyFoundCount === 0, `Error: Unexpected filtered key ${propertyKey}`) + .to.be.true; + continue; + } + } + + expect(propertyFoundCount === 1, `Error: The property with key ${propertyKey} is not found`) + .to.be.true; + } +} + +export async function lockCollection( + api: ApiPromise, + issuerUri: string, + collectionId: number, + max = 0, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkCore.lockCollection(collectionId); + const events = await executeTransaction(api, issuer, tx); + const lockResult = extractRmrkCoreTxResult(events, 'CollectionLocked', (data) => { + return parseInt(data[1].toString(), 10); + }); + expect(lockResult.success, 'Error: Unable to lock a collection').to.be.true; + expect(lockResult.successData!, 'Error: Invalid collection was locked') + .to.be.eq(collectionId); + + await getCollection(api, collectionId).then((collectionOption) => { + const collection = collectionOption.unwrap(); + expect(collection.max.unwrap().toNumber()).to.be.equal(max); + }); +} + +export async function setPropertyCollection( + api: ApiPromise, + issuerUri: string, + collectionId: number, + key: string, + value: string, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const alice = privateKey(issuerUri, Number(ss58Format)); + + const tx = api.tx.rmrkCore.setProperty(collectionId, null, key, value); + const events = await executeTransaction(api, alice, tx); + const propResult = extractRmrkCoreTxResult(events, 'PropertySet', (data) => { + return { + collectionId: parseInt(data[0].toString(), 10), + nftId: data[1] as Option, + key: data[2] as Bytes, + value: data[3] as Bytes, + }; + }); + + expect(propResult.success, 'Error: Unable to set collection property').to.be.true; + const eventData = propResult.successData!; + const eventDescription = 'from set collection property event'; + + expect(eventData.collectionId, 'Error: Invalid collection ID ' + eventDescription) + .to.be.equal(collectionId); + + expect(eventData.nftId.eq(null), 'Error: Unexpected NFT ID ' + eventDescription) + .to.be.true; + + expect(eventData.key.eq(key), 'Error: Invalid property key ' + eventDescription) + .to.be.true; + + expect(eventData.value.eq(value), 'Error: Invalid property value ' + eventDescription) + .to.be.true; + + expect(await isCollectionPropertyExists(api, collectionId, key, value)) + .to.be.true; +} + +export async function burnNft( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const maxBurns = 10; + const tx = api.tx.rmrkCore.burnNft(collectionId, nftId, maxBurns); + const events = await executeTransaction(api, issuer, tx); + const burnResult = extractRmrkCoreTxResult(events, 'NFTBurned', (data) => { + return parseInt(data[1].toString(), 10); + }); + + expect(burnResult.success, 'Error: Unable to burn an NFT').to.be.true; + expect(burnResult.successData!, 'Error: Invalid NFT was burned') + .to.be.eq(nftId); + + const nftBurned = await getNft(api, collectionId, nftId); + expect(nftBurned.isSome).to.be.false; +} + +export async function acceptNftResource( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, + resourceId: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const tx = api.tx.rmrkCore.acceptResource( + collectionId, + nftId, + resourceId, + ); + + const events = await executeTransaction(api, issuer, tx); + const acceptResult = extractRmrkCoreTxResult(events, 'ResourceAccepted', (data) => { + return { + nftId: parseInt(data[0].toString(), 10), + resourceId: parseInt(data[1].toString(), 10), + }; + }); + + expect(acceptResult.success, 'Error: Unable to accept a resource').to.be.true; + expect(acceptResult.successData!.nftId, 'Error: Invalid NFT ID while accepting a resource') + .to.be.eq(nftId); + expect(acceptResult.successData!.resourceId, 'Error: Invalid resource ID while accepting a resource') + .to.be.eq(resourceId); + + const resource = await getResourceById(api, collectionId, nftId, resourceId); + checkResourceStatus(resource, 'added'); +} + +async function executeResourceCreation( + api: ApiPromise, + issuer: IKeyringPair, + tx: any, + collectionId: number, + nftId: number, + expectedStatus: 'pending' | 'added', +): Promise { + const events = await executeTransaction(api, issuer, tx); + + const resourceResult = extractRmrkCoreTxResult(events, 'ResourceAdded', (data) => { + return parseInt(data[1].toString(), 10); + }); + expect(resourceResult.success, 'Error: Unable to add resource').to.be.true; + const resourceId = resourceResult.successData!; + + const resource = await getResourceById(api, collectionId, nftId, resourceId); + checkResourceStatus(resource, expectedStatus); + + return resource; +} + +export async function addNftBasicResource( + api: ApiPromise, + issuerUri: string, + expectedStatus: 'pending' | 'added', + collectionId: number, + nftId: number, + src: string | null, + metadata: string | null, + license: string | null, + thumb: string | null, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const basicResource = api.createType('RmrkTraitsResourceBasicResource', { + src: src, + metadata: metadata, + license: license, + thumb: thumb, + }) as BasicResource; + + const tx = api.tx.rmrkCore.addBasicResource( + collectionId, + nftId, + basicResource, + ); + + const resource = await executeResourceCreation(api, issuer, tx, collectionId, nftId, expectedStatus); + + // FIXME A workaround. It seems it is a PolkadotJS bug. + // All of the following are `false`. + // + // console.log('>>> basic:', resource.resource.isBasic); + // console.log('>>> composable:', resource.resource.isComposable); + // console.log('>>> slot:', resource.resource.isSlot); + const resourceJson = resource.resource.toHuman() as any; + + expect(Object.prototype.hasOwnProperty.call(resourceJson, 'Basic'), 'Error: Expected basic resource type') + .to.be.true; + + const recvBasicRes = resourceJson['Basic']; + + expect(recvBasicRes.src, 'Error: Invalid basic resource src') + .to.be.eq(src); + expect(recvBasicRes.metadata, 'Error: basic first resource metadata') + .to.be.eq(metadata); + expect(recvBasicRes.license, 'Error: basic first resource license') + .to.be.eq(license); + expect(recvBasicRes.thumb, 'Error: basic first resource thumb') + .to.be.eq(thumb); + + return resource.id.toNumber(); +} + +export async function addNftComposableResource( + api: ApiPromise, + issuerUri: string, + expectedStatus: 'pending' | 'added', + collectionId: number, + nftId: number, + parts: number[], + baseId: number, + src: string | null, + metadata: string | null, + license: string | null, + thumb: string | null, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const composableResource = api.createType('RmrkTraitsResourceComposableResource', { + parts: parts, // api.createType('Vec', parts), + base: baseId, + src: src, + metadata: metadata, + license: license, + thumb: thumb, + }) as ComposableResource; + + const tx = api.tx.rmrkCore.addComposableResource( + collectionId, + nftId, + composableResource, + ); + + const resource = await executeResourceCreation(api, issuer, tx, collectionId, nftId, expectedStatus); + + // FIXME A workaround. It seems it is a PolkadotJS bug. + // All of the following are `false`. + // + // console.log('>>> basic:', resource.resource.isBasic); + // console.log('>>> composable:', resource.resource.isComposable); + // console.log('>>> slot:', resource.resource.isSlot); + const resourceJson = resource.resource.toHuman() as any; + + expect(Object.prototype.hasOwnProperty.call(resourceJson, 'Composable'), 'Error: Expected composable resource type') + .to.be.true; + + const recvComposableRes = resourceJson['Composable']; + + expect(recvComposableRes.parts.toString(), 'Error: Invalid composable resource parts') + .to.be.eq(parts.toString()); + expect(recvComposableRes.base, 'Error: Invalid composable resource base id') + .to.be.eq(baseId.toString()); + expect(recvComposableRes.src, 'Error: Invalid composable resource src') + .to.be.eq(src); + expect(recvComposableRes.metadata, 'Error: Invalid composable resource metadata') + .to.be.eq(metadata); + expect(recvComposableRes.license, 'Error: Invalid composable resource license') + .to.be.eq(license); + expect(recvComposableRes.thumb, 'Error: Invalid composable resource thumb') + .to.be.eq(thumb); + + return resource.id.toNumber(); +} + +export async function addNftSlotResource( + api: ApiPromise, + issuerUri: string, + expectedStatus: 'pending' | 'added', + collectionId: number, + nftId: number, + baseId: number, + slotId: number, + src: string | null, + metadata: string | null, + license: string | null, + thumb: string | null, +): Promise { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const slotResource = api.createType('RmrkTraitsResourceSlotResource', { + base: baseId, + src: src, + metadata, + slot: slotId, + license: license, + thumb: thumb, + }) as SlotResource; + + const tx = api.tx.rmrkCore.addSlotResource( + collectionId, + nftId, + slotResource, + ); + + const resource = await executeResourceCreation(api, issuer, tx, collectionId, nftId, expectedStatus); + + // FIXME A workaround. It seems it is a PolkadotJS bug. + // All of the following are `false`. + // + // console.log('>>> basic:', resource.resource.isBasic); + // console.log('>>> composable:', resource.resource.isComposable); + // console.log('>>> slot:', resource.resource.isSlot); + const resourceJson = resource.resource.toHuman() as any; + + expect(Object.prototype.hasOwnProperty.call(resourceJson, 'Slot'), 'Error: Expected slot resource type') + .to.be.true; + + const recvSlotRes = resourceJson['Slot']; + + expect(recvSlotRes.base, 'Error: Invalid slot resource base id') + .to.be.eq(baseId.toString()); + expect(recvSlotRes.slot, 'Error: Invalid slot resource slot id') + .to.be.eq(slotId.toString()); + expect(recvSlotRes.src, 'Error: Invalid slot resource src') + .to.be.eq(src); + expect(recvSlotRes.metadata, 'Error: Invalid slot resource metadata') + .to.be.eq(metadata); + expect(recvSlotRes.license, 'Error: Invalid slot resource license') + .to.be.eq(license); + expect(recvSlotRes.thumb, 'Error: Invalid slot resource thumb') + .to.be.eq(thumb); + + return resource.id.toNumber(); +} + +export async function equipNft( + api: ApiPromise, + issuerUri: string, + item: NftIdTuple, + equipper: NftIdTuple, + resource: number, + base: number, + slot: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkEquip.equip(item, equipper, resource, base, slot); + const events = await executeTransaction(api, issuer, tx); + const equipResult = extractRmrkEquipTxResult(events, 'SlotEquipped', (data) => { + return { + item_collection: parseInt(data[0].toString(), 10), + item_nft: parseInt(data[1].toString(), 10), + base_id: parseInt(data[2].toString(), 10), + slot_id: parseInt(data[3].toString(), 10), + }; + }); + expect(equipResult.success, 'Error: Unable to equip an item').to.be.true; + expect(equipResult.successData!.item_collection, 'Error: Invalid item collection id') + .to.be.eq(item[0]); + expect(equipResult.successData!.item_nft, 'Error: Invalid item NFT id') + .to.be.eq(item[1]); + expect(equipResult.successData!.base_id, 'Error: Invalid base id') + .to.be.eq(base); + expect(equipResult.successData!.slot_id, 'Error: Invalid slot id') + .to.be.eq(slot); +} + +export async function unequipNft( + api: ApiPromise, + issuerUri: string, + item: any, + equipper: any, + resource: number, + base: number, + slot: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + const tx = api.tx.rmrkEquip.equip(item, equipper, resource, base, slot); + const events = await executeTransaction(api, issuer, tx); + + const unEquipResult = extractRmrkEquipTxResult( + events, + 'SlotUnequipped', + (data) => { + return { + item_collection: parseInt(data[0].toString(), 10), + item_nft: parseInt(data[1].toString(), 10), + base_id: parseInt(data[2].toString(), 10), + slot_id: parseInt(data[3].toString(), 10), + }; + }, + ); + + expect(unEquipResult.success, 'Error: Unable to unequip an item').to.be.true; + expect(unEquipResult.successData!.item_collection, 'Error: Invalid item collection id') + .to.be.eq(item[0]); + expect(unEquipResult.successData!.item_nft, 'Error: Invalid item NFT id') + .to.be.eq(item[1]); + expect(unEquipResult.successData!.base_id, 'Error: Invalid base id') + .to.be.eq(base); + expect(unEquipResult.successData!.slot_id, 'Error: Invalid slot id') + .to.be.eq(slot); +} + +export async function removeNftResource( + api: ApiPromise, + expectedStatus: 'pending' | 'removed', + issuerUri: string, + collectionId: number, + nftId: number, + resourceId: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const tx = api.tx.rmrkCore.removeResource(collectionId, nftId, resourceId); + const events = await executeTransaction(api, issuer, tx); + const removeResult = extractRmrkCoreTxResult(events, 'ResourceRemoval', (data) => { + return { + nftId: parseInt(data[0].toString(), 10), + resourceId: parseInt(data[1].toString(), 10), + }; + }); + expect(removeResult.success, 'Error: Unable to remove a resource').to.be.true; + expect(removeResult.successData!.nftId, 'Error: Invalid NFT Id while removing a resource') + .to.be.eq(nftId); + expect(removeResult.successData!.resourceId, 'Error: Invalid resource Id while removing a resource') + .to.be.eq(resourceId); + + const afterDeleting = await findResourceById(api, collectionId, nftId, resourceId); + + if (expectedStatus === 'pending') { + expect(afterDeleting).not.to.be.null; + expect(afterDeleting?.pendingRemoval.isTrue).to.be.equal(true); + } else { + expect(afterDeleting).to.be.null; + } +} + +export async function acceptResourceRemoval( + api: ApiPromise, + issuerUri: string, + collectionId: number, + nftId: number, + resourceId: number, +) { + const ss58Format = api.registry.getChainProperties()!.toJSON().ss58Format; + const issuer = privateKey(issuerUri, Number(ss58Format)); + + const tx = api.tx.rmrkCore.acceptResourceRemoval(collectionId, nftId, resourceId); + const events = await executeTransaction(api, issuer, tx); + const acceptResult = extractRmrkCoreTxResult(events, 'ResourceRemovalAccepted', (data) => { + return { + nftId: parseInt(data[0].toString(), 10), + resourceId: parseInt(data[1].toString(), 10), + }; + }); + expect(acceptResult.success, 'Error: Unable to accept a resource').to.be.true; + expect(acceptResult.successData!.nftId, 'Error: Invalid NFT Id while accepting a resource') + .to.be.eq(nftId); + expect(acceptResult.successData!.resourceId, 'Error: Invalid resource Id while accepting a resource') + .to.be.eq(resourceId); + + const afterDeleting = await findResourceById(api, collectionId, nftId, resourceId); + expect(afterDeleting, 'Error: resource deleting failed').to.be.null; +} diff --git a/tests/src/substrate/substrate-api.ts b/tests/src/substrate/substrate-api.ts index b2437549ba..cf6493cd73 100644 --- a/tests/src/substrate/substrate-api.ts +++ b/tests/src/substrate/substrate-api.ts @@ -59,6 +59,17 @@ function defaultApiOptions(): ApiOptions { }; } +export async function getApiConnection(settings: ApiOptions | undefined = undefined): Promise { + settings = settings || defaultApiOptions(); + const api = new ApiPromise(settings); + + if (api) { + await api.isReadyOrError; + } + + return api; +} + export default async function usingApi(action: (api: ApiPromise, privateKeyWrapper: (account: string) => IKeyringPair) => Promise, settings: ApiOptions | undefined = undefined): Promise { settings = settings || defaultApiOptions(); const api: ApiPromise = new ApiPromise(settings); diff --git a/tests/src/util/helpers.ts b/tests/src/util/helpers.ts index 9e1b06157f..a2c0ff054f 100644 --- a/tests/src/util/helpers.ts +++ b/tests/src/util/helpers.ts @@ -830,6 +830,25 @@ export async function burnItemExpectSuccess(sender: IKeyringPair, collectionId: }); } +export async function burnItemExpectFailure(sender: IKeyringPair, collectionId: number, tokenId: number, value: number | bigint = 1) { + await usingApi(async (api) => { + const tx = api.tx.unique.burnItem(collectionId, tokenId, value); + + const events = await expect(submitTransactionExpectFailAsync(sender, tx)).to.be.rejected; + const result = getCreateCollectionResult(events); + // tslint:disable-next-line:no-unused-expression + expect(result.success).to.be.false; + }); +} + +export async function burnFromExpectSuccess(sender: IKeyringPair, from: IKeyringPair | CrossAccountId, collectionId: number, tokenId: number, value: number | bigint = 1) { + await usingApi(async (api) => { + const tx = api.tx.unique.burnFrom(collectionId, normalizeAccountId(from), tokenId, value); + const events = await submitTransactionAsync(sender, tx); + return getGenericResult(events).success; + }); +} + export async function approve( api: ApiPromise, @@ -1005,7 +1024,7 @@ scheduleExpectSuccess( expectedBlockNumber, repetitions > 1 ? [period, repetitions] : null, 0, - {value: operationTx as any}, + {Value: operationTx as any}, ); const events = await submitTransactionAsync(sender, scheduleTx); @@ -1032,7 +1051,7 @@ scheduleExpectFailure( expectedBlockNumber, repetitions <= 1 ? null : [period, repetitions], 0, - {value: operationTx as any}, + {Value: operationTx as any}, ); //const events = @@ -1361,7 +1380,7 @@ export async function createItemWithPropsExpectFailure(sender: IKeyringPair, col let tx; if (createMode === 'NFT') { - const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}}); + const data = api.createType('UpDataStructsCreateItemData', {NFT: {properties: props}}) as UpDataStructsCreateItemData; tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), data); } else { tx = api.tx.unique.createItem(collectionId, normalizeAccountId(owner), createMode); @@ -1630,3 +1649,17 @@ export async function waitNewBlocks(blocksCount = 1): Promise { return promise; }); } + +export async function repartitionRFT( + api: ApiPromise, + collectionId: number, + sender: IKeyringPair, + tokenId: number, + amount: bigint, +): Promise { + const tx = api.tx.unique.repartition(collectionId, tokenId, amount); + const events = await submitTransactionAsync(sender, tx); + const result = getGenericResult(events); + + return result.success; +}