difftreelog
Merge pull request #848 from UniqueNetwork/fix/find-parent
in: master
15 files changed
pallets/common/src/erc.rsdiffbeforeafterboth28use sp_std::{vec, vec::Vec};28use sp_std::{vec, vec::Vec};29use sp_core::U256;29use sp_core::U256;30use up_data_structs::{30use up_data_structs::{31 AccessMode, CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property,31 CollectionMode, CollectionPermissions, OwnerRestrictedSet, Property, SponsoringRateLimit,32 SponsoringRateLimit, SponsorshipState,32 SponsorshipState,33};33};3434pallets/common/src/eth.rsdiffbeforeafterboth80 if cross_account_id.is_canonical_substrate() {80 if cross_account_id.is_canonical_substrate() {81 Self::from_sub::<T>(cross_account_id.as_sub())81 Self::from_sub::<T>(cross_account_id.as_sub())82 } else {82 } else {83 Self {83 Self::from_eth(*cross_account_id.as_eth())84 eth: *cross_account_id.as_eth(),85 sub: Default::default(),86 }87 }84 }88 }85 }89 /// Creates [`CrossAddress`] from Substrate account.86 /// Creates [`CrossAddress`] from Substrate account.97 sub: U256::from_big_endian(account_id.as_ref()),94 sub: U256::from_big_endian(account_id.as_ref()),98 }95 }99 }96 }97 /// Creates [`CrossAddress`] from Ethereum account.98 pub fn from_eth(address: Address) -> Self {99 Self {100 eth: address,101 sub: Default::default(),102 }103 }100 /// Converts [`CrossAddress`] to `CrossAccountId`.104 /// Converts [`CrossAddress`] to `CrossAccountId`.101 pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>105 pub fn into_sub_cross_account<T>(&self) -> evm_coder::execution::Result<T::CrossAccountId>102 where106 wherepallets/common/src/lib.rsdiffbeforeafterboth100 PropertyValue,100 PropertyValue,101 PropertyPermission,101 PropertyPermission,102 PropertiesError,102 PropertiesError,103 TokenOwnerError,103 PropertyKeyPermission,104 PropertyKeyPermission,104 TokenData,105 TokenData,105 TrySetProperty,106 TrySetProperty,2134 /// Get the owner of the token.2135 /// Get the owner of the token.2135 ///2136 ///2136 /// * `token` - The token for which you need to find out the owner.2137 /// * `token` - The token for which you need to find out the owner.2137 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId>;2138 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError>;213821392139 /// Returns 10 tokens owners in no particular order.2140 /// Returns 10 tokens owners in no particular order.2140 ///2141 ///pallets/fungible/src/common.rsdiffbeforeafterboth181819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};20use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};20use up_data_structs::{21 TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData, TokenOwnerError,22};21use pallet_common::{23use pallet_common::{22 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,24 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,404 TokenId::default()406 TokenId::default()405 }407 }406408407 fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {409 fn token_owner(&self, _token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {408 None410 Err(TokenOwnerError::MultipleOwners)409 }411 }410412411 /// Returns 10 tokens owners in no particular order.413 /// Returns 10 tokens owners in no particular order.pallets/nonfungible/src/common.rsdiffbeforeafterboth19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};19use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight};20use up_data_structs::{20use up_data_structs::{21 TokenId, CreateItemExData, CollectionId, budget::Budget, Property, PropertyKey,21 TokenId, CreateItemExData, CollectionId, budget::Budget, Property, PropertyKey,22 PropertyKeyPermission, PropertyValue,22 PropertyKeyPermission, PropertyValue, TokenOwnerError,23};23};24use pallet_common::{24use pallet_common::{25 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,25 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,460 TokenId(<TokensMinted<T>>::get(self.id))460 TokenId(<TokensMinted<T>>::get(self.id))461 }461 }462462463 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId> {463 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {464 <TokenData<T>>::get((self.id, token)).map(|t| t.owner)464 <TokenData<T>>::get((self.id, token))465 .map(|t| t.owner)466 .ok_or(TokenOwnerError::NotFound)465 }467 }466468467 /// Returns token owners.469 /// Returns token owners.468 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {470 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {469 self.token_owner(token).map_or_else(|| vec![], |t| vec![t])471 self.token_owner(token).map_or_else(|_| vec![], |t| vec![t])470 }472 }471473472 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {474 fn token_property(&self, token_id: TokenId, key: &PropertyKey) -> Option<PropertyValue> {pallets/nonfungible/src/erc.rsdiffbeforeafterboth728 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {728 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {729 Self::token_owner(&self, token_id.try_into()?)729 Self::token_owner(&self, token_id.try_into()?)730 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))730 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))731 .ok_or(Error::Revert("key too large".into()))731 .map_err(|_| Error::Revert("token not found".into()))732 }732 }733733734 /// Returns the token properties.734 /// Returns the token properties.pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth742 &target_nft_budget,742 &target_nft_budget,743 )743 )744 .map_err(Self::map_unique_err_to_proxy)?;744 .map_err(Self::map_unique_err_to_proxy)?745 .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;745746746 approval_required = cross_sender != target_nft_owner;747 approval_required = cross_sender != target_nft_owner;747748990 let nft_owner =991 let nft_owner =991 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)992 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)992 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;993 .map_err(|_| <Error<T>>::ResourceDoesntExist)?994 .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;993995994 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {996 Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {995 ensure!(res.pending, <Error<T>>::ResourceNotPending);997 ensure!(res.pending, <Error<T>>::ResourceNotPending);1045 let nft_owner =1047 let nft_owner =1046 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1048 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1047 .map_err(|_| <Error<T>>::ResourceDoesntExist)?;1049 .map_err(|_| <Error<T>>::ResourceDoesntExist)?1050 .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;104810511049 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);1052 ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);10501053166716701668 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1671 let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)1669 .map_err(Self::map_unique_err_to_proxy)?;1672 .map_err(Self::map_unique_err_to_proxy)?1673 .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;167016741671 let pending = sender != nft_owner;1675 let pending = sender != nft_owner;167216761721 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1725 let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);1722 let topmost_owner =1726 let topmost_owner =1723 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;1727 <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?1728 .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;172417291725 let sender = T::CrossAccountId::from_sub(sender);1730 let sender = T::CrossAccountId::from_sub(sender);1726 if topmost_owner == sender {1731 if topmost_owner == sender {pallets/proxy-rmrk-core/src/rpc.rsdiffbeforeafterboth68 }68 }696970 let owner = match collection.token_owner(nft_id) {70 let owner = match collection.token_owner(nft_id) {71 Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {71 Ok(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {72 Some((col, tok)) => {72 Some((col, tok)) => {73 let rmrk_collection = <Pallet<T>>::rmrk_collection_id(col)?;73 let rmrk_collection = <Pallet<T>>::rmrk_collection_id(col)?;747475 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)75 RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)76 }76 }77 None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone()),77 None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone()),78 },78 },79 None => return Ok(None),79 _ => return Ok(None),80 };80 };818182 Ok(Some(RmrkInstanceInfo {82 Ok(Some(RmrkInstanceInfo {pallets/refungible/src/common.rsdiffbeforeafterboth21use up_data_structs::{21use up_data_structs::{22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,22 CollectionId, TokenId, CreateItemExData, budget::Budget, Property, PropertyKey, PropertyValue,23 PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners,23 PropertyKeyPermission, CollectionPropertiesVec, CreateRefungibleExMultipleOwners,24 CreateRefungibleExSingleOwner,24 CreateRefungibleExSingleOwner, TokenOwnerError,25};25};26use pallet_common::{26use pallet_common::{27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,27 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,478 TokenId(<TokensMinted<T>>::get(self.id))478 TokenId(<TokensMinted<T>>::get(self.id))479 }479 }480480481 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId> {481 fn token_owner(&self, token: TokenId) -> Result<T::CrossAccountId, TokenOwnerError> {482 <Pallet<T>>::token_owner(self.id, token)482 <Pallet<T>>::token_owner(self.id, token)483 }483 }484484pallets/refungible/src/erc.rsdiffbeforeafterboth43use sp_std::{collections::btree_map::BTreeMap, vec::Vec, vec};43use sp_std::{collections::btree_map::BTreeMap, vec::Vec, vec};44use up_data_structs::{44use up_data_structs::{45 CollectionId, CollectionPropertiesVec, mapping::TokenAddressMapping, Property, PropertyKey,45 CollectionId, CollectionPropertiesVec, mapping::TokenAddressMapping, Property, PropertyKey,46 PropertyKeyPermission, PropertyPermission, TokenId,46 PropertyKeyPermission, PropertyPermission, TokenId, TokenOwnerError,47};47};484849use crate::{49use crate::{411 self.consume_store_reads(2)?;411 self.consume_store_reads(2)?;412 let token = token_id.try_into()?;412 let token = token_id.try_into()?;413 let owner = <Pallet<T>>::token_owner(self.id, token);413 let owner = <Pallet<T>>::token_owner(self.id, token);414 Ok(owner414 owner415 .map(|address| *address.as_eth())415 .map(|address| *address.as_eth())416 .unwrap_or_else(|| ADDRESS_FOR_PARTIALLY_OWNED_TOKENS))416 .or_else(|err| match err {417 TokenOwnerError::NotFound => Err(Error::Revert("token not found".into())),418 TokenOwnerError::MultipleOwners => Ok(ADDRESS_FOR_PARTIALLY_OWNED_TOKENS),419 })417 }420 }418421419 /// @dev Not implemented422 /// @dev Not implemented766 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {769 fn cross_owner_of(&self, token_id: U256) -> Result<eth::CrossAddress> {767 Self::token_owner(&self, token_id.try_into()?)770 Self::token_owner(&self, token_id.try_into()?)768 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))771 .map(|o| eth::CrossAddress::from_sub_cross_account::<T>(&o))769 .ok_or(Error::Revert("key too large".into()))772 .or_else(|err| match err {773 TokenOwnerError::NotFound => Err(Error::Revert("token not found".into())),774 TokenOwnerError::MultipleOwners => Ok(eth::CrossAddress::from_eth(775 ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,776 )),777 })770 }778 }771779772 /// Returns the token properties.780 /// Returns the token properties.pallets/refungible/src/lib.rsdiffbeforeafterboth107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,107 AccessMode, budget::Budget, CollectionId, CollectionFlags, CreateCollectionData,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,108 mapping::TokenAddressMapping, MAX_REFUNGIBLE_PIECES, Property, PropertyKey,109 PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId,109 PropertyKeyPermission, PropertyPermission, PropertyScope, PropertyValue, TokenId,110 TrySetProperty, PropertiesPermissionMap, CreateRefungibleExMultipleOwners,110 TrySetProperty, PropertiesPermissionMap, CreateRefungibleExMultipleOwners, TokenOwnerError,111};111};112112113pub use pallet::*;113pub use pallet::*;480 <Balance<T>>::remove((collection.id, token, owner));480 <Balance<T>>::remove((collection.id, token, owner));481 <AccountBalance<T>>::insert((collection.id, owner), account_balance);481 <AccountBalance<T>>::insert((collection.id, owner), account_balance);482482483 if let Some(user) = Self::token_owner(collection.id, token) {483 if let Ok(user) = Self::token_owner(collection.id, token) {484 <PalletEvm<T>>::deposit_log(484 <PalletEvm<T>>::deposit_log(485 ERC721Events::Transfer {485 ERC721Events::Transfer {486 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,486 from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS,1365 Ok(())1365 Ok(())1366 }1366 }136713671368 fn token_owner(collection_id: CollectionId, token_id: TokenId) -> Option<T::CrossAccountId> {1368 fn token_owner(1369 collection_id: CollectionId,1370 token_id: TokenId,1371 ) -> Result<T::CrossAccountId, TokenOwnerError> {1369 let mut owner = None;1372 let mut owner = None;1370 let mut count = 0;1373 let mut count = 0;1371 for key in Balance::<T>::iter_key_prefix((collection_id, token_id)) {1374 for key in Balance::<T>::iter_key_prefix((collection_id, token_id)) {1372 count += 1;1375 count += 1;1373 if count > 1 {1376 if count > 1 {1374 return None;1377 return Err(TokenOwnerError::MultipleOwners);1375 }1378 }1376 owner = Some(key);1379 owner = Some(key);1377 }1380 }1378 owner1381 owner.ok_or(TokenOwnerError::NotFound)1379 }1382 }138013831381 fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {1384 fn total_pieces(collection_id: CollectionId, token_id: TokenId) -> Option<u128> {pallets/structure/src/lib.rsdiffbeforeafterboth62pub use pallet::*;62pub use pallet::*;63use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};63use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};64use up_data_structs::{CollectionId, TokenId, mapping::TokenAddressMapping, budget::Budget};64use up_data_structs::{65 CollectionId, TokenId, mapping::TokenAddressMapping, budget::Budget, TokenOwnerError,66};656766#[cfg(feature = "runtime-benchmarks")]68#[cfg(feature = "runtime-benchmarks")]135 User(CrossAccountId),137 User(CrossAccountId),136 /// Could not find the token provided as the owner.138 /// Could not find the token provided as the owner.137 TokenNotFound,139 TokenNotFound,140 /// Nested token has multiple owners.141 MultipleOwners,138 /// Token owner is another token (still, the target token may not exist).142 /// Token owner is another token (still, the target token may not exist).139 Token(CollectionId, TokenId),143 Token(CollectionId, TokenId),140}144}159 let handle = handle.as_dyn();163 let handle = handle.as_dyn();160164161 Ok(match handle.token_owner(token) {165 Ok(match handle.token_owner(token) {162 Some(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {166 Ok(owner) => match T::CrossTokenAddressMapping::address_to_token(&owner) {163 Some((collection, token)) => Parent::Token(collection, token),167 Some((collection, token)) => Parent::Token(collection, token),164 None => Parent::User(owner),168 None => Parent::User(owner),165 },169 },170 Err(TokenOwnerError::MultipleOwners) => Parent::MultipleOwners,166 None => Parent::TokenNotFound,171 Err(TokenOwnerError::NotFound) => Parent::TokenNotFound,167 })172 })168 }173 }169174202 /// Try to dereference address, until finding top level owner207 /// Try to dereference address, until finding top level owner203 ///208 ///204 /// May return token address if parent token not yet exists209 /// May return token address if parent token not yet exists210 ///211 /// Returns `None` if the token has multiple owners.205 ///212 ///206 /// - `budget`: Limit for searching parents in depth.213 /// - `budget`: Limit for searching parents in depth.207 pub fn find_topmost_owner(214 pub fn find_topmost_owner(208 collection: CollectionId,215 collection: CollectionId,209 token: TokenId,216 token: TokenId,210 budget: &dyn Budget,217 budget: &dyn Budget,211 ) -> Result<T::CrossAccountId, DispatchError> {218 ) -> Result<Option<T::CrossAccountId>, DispatchError> {212 let owner = Self::parent_chain(collection, token)219 let owner = Self::parent_chain(collection, token)213 .take_while(|_| budget.consume())220 .take_while(|_| budget.consume())214 .find(|p| matches!(p, Ok(Parent::User(_) | Parent::TokenNotFound)))221 .find(|p| {222 matches!(223 p,224 Ok(Parent::User(_) | Parent::TokenNotFound | Parent::MultipleOwners)225 )226 })215 .ok_or(<Error<T>>::DepthLimit)??;227 .ok_or(<Error<T>>::DepthLimit)??;216228217 Ok(match owner {229 Ok(match owner {218 Parent::User(v) => v,230 Parent::User(v) => Some(v),231 Parent::MultipleOwners => None,219 _ => fail!(<Error<T>>::TokenNotFound),232 _ => fail!(<Error<T>>::TokenNotFound),220 })233 })221 }234 }222235223 /// Find the topmost parent and check that assigning `for_nest` token as a child for236 /// Find the topmost parent and check that assigning `for_nest` token as a child for224 /// `token` wouldn't create a cycle.237 /// `token` wouldn't create a cycle.238 ///239 /// Returns `None` if the token has multiple owners.225 ///240 ///226 /// - `budget`: Limit for searching parents in depth.241 /// - `budget`: Limit for searching parents in depth.227 pub fn get_checked_topmost_owner(242 pub fn get_checked_topmost_owner(228 collection: CollectionId,243 collection: CollectionId,229 token: TokenId,244 token: TokenId,230 for_nest: Option<(CollectionId, TokenId)>,245 for_nest: Option<(CollectionId, TokenId)>,231 budget: &dyn Budget,246 budget: &dyn Budget,232 ) -> Result<T::CrossAccountId, DispatchError> {247 ) -> Result<Option<T::CrossAccountId>, DispatchError> {233 // Tried to nest token in itself248 // Tried to nest token in itself234 if Some((collection, token)) == for_nest {249 if Some((collection, token)) == for_nest {235 return Err(<Error<T>>::OuroborosDetected.into());250 return Err(<Error<T>>::OuroborosDetected.into());242 return Err(<Error<T>>::OuroborosDetected.into())257 return Err(<Error<T>>::OuroborosDetected.into())243 }258 }244 // Token is owned by other user259 // Token is owned by other user245 Parent::User(user) => return Ok(user),260 Parent::User(user) => return Ok(Some(user)),246 Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),261 Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),262 Parent::MultipleOwners => return Ok(None),247 // Continue parent chain263 // Continue parent chain248 Parent::Token(_, _) => {}264 Parent::Token(_, _) => {}249 }265 }284 budget: &dyn Budget,300 budget: &dyn Budget,285 ) -> Result<bool, DispatchError> {301 ) -> Result<bool, DispatchError> {286 let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {302 let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {287 Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,303 Some((collection, token)) => match Self::find_topmost_owner(collection, token, budget)?304 {305 Some(topmost_owner) => topmost_owner,306 None => return Ok(false),307 },288 None => user,308 None => user,289 };309 };290310291 Self::get_checked_topmost_owner(collection, token, for_nest, budget)311 Self::get_checked_topmost_owner(collection, token, for_nest, budget).map(|indirect_owner| {292 .map(|indirect_owner| indirect_owner == target_parent)312 indirect_owner.map_or(false, |indirect_owner| indirect_owner == target_parent)313 })293 }314 }294315295 /// Checks that `under` is valid token and that `token_id` could be nested under it316 /// Checks that `under` is valid token and that `token_id` could be nested under itprimitives/data-structs/src/lib.rsdiffbeforeafterboth1099 EmptyPropertyKey,1099 EmptyPropertyKey,1100}1100}11011102/// Token owner error: it could be either `NotFound` ot `MultipleOwners`.1103#[derive(Debug)]1104pub enum TokenOwnerError {1105 NotFound,1106 MultipleOwners,1107}110111081102/// Marker for scope of property.1109/// Marker for scope of property.1103///1110///runtime/common/runtime_apis.rsdiffbeforeafterboth161617#[macro_export]17#[macro_export]18macro_rules! dispatch_unique_runtime {18macro_rules! dispatch_unique_runtime {19 ($collection:ident.$method:ident($($name:ident),*)) => {{19 ($collection:ident.$method:ident($($name:ident),*) $($rest:tt)*) => {{20 let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);20 let collection = <Runtime as pallet_common::Config>::CollectionDispatch::dispatch(<pallet_common::CollectionHandle<Runtime>>::try_get($collection)?);21 let dispatch = collection.as_dyn();21 let dispatch = collection.as_dyn();222223 Ok::<_, DispatchError>(dispatch.$method($($name),*))23 Ok::<_, DispatchError>(dispatch.$method($($name),*) $($rest)*)24 }};24 }};25}25}262673 }73 }747475 fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {75 fn token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {76 dispatch_unique_runtime!(collection.token_owner(token))76 dispatch_unique_runtime!(collection.token_owner(token).ok())77 }77 }787879 fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError> {79 fn token_owners(collection: CollectionId, token: TokenId) -> Result<Vec::<CrossAccountId>, DispatchError> {83 fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {83 fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {84 let budget = up_data_structs::budget::Value::new(10);84 let budget = up_data_structs::budget::Value::new(10);858586 Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))86 Ok(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?)87 }87 }88 fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {88 fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {89 Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))89 Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))tests/src/nesting/nest.test.tsdiffbeforeafterboth15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.161617import {IKeyringPair} from '@polkadot/types/types';17import {IKeyringPair} from '@polkadot/types/types';18import {expect, itSub, usingPlaygrounds} from '../util';18import {expect, itSub, Pallets, usingPlaygrounds} from '../util';191920describe('Integration Test: Composite nesting tests', () => {20describe('Integration Test: Composite nesting tests', () => {21 let alice: IKeyringPair;21 let alice: IKeyringPair;138 before(async () => {138 before(async () => {139 await usingPlaygrounds(async (helper, privateKey) => {139 await usingPlaygrounds(async (helper, privateKey) => {140 const donor = await privateKey({filename: __filename});140 const donor = await privateKey({filename: __filename});141 [alice, bob, charlie] = await helper.arrange.createAccounts([50n, 10n, 10n], donor);141 [alice, bob, charlie] = await helper.arrange.createAccounts([200n, 10n, 10n], donor);142 });142 });143 });143 });144144289 expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(7n);289 expect(await collectionFT.getBalance(targetToken.nestingAccount())).to.be.equal(7n);290 });290 });291292 itSub.ifWithPallets('ReFungible: getTopmostOwner works correctly with Nesting', [Pallets.ReFungible], async({helper}) => {293 const collectionNFT = await helper.nft.mintCollection(alice, {294 permissions: {295 nesting: {296 tokenOwner: true,297 },298 },299 });300 const collectionRFT = await helper.rft.mintCollection(alice);301302 const nft = await collectionNFT.mintToken(alice, {Substrate: alice.address});303 const rft = await collectionRFT.mintToken(alice, 100n, {Substrate: alice.address});304305 expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});306307 await rft.transfer(alice, nft.nestingAccount(), 40n);308309 expect(await rft.getTopmostOwner()).deep.equal(null);310311 await rft.transfer(alice, nft.nestingAccount(), 60n);312313 expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});314315 await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 30n);316317 expect(await rft.getTopmostOwner()).deep.equal(null);318319 await rft.transferFrom(alice, nft.nestingAccount(), {Substrate: alice.address}, 70n);320321 expect(await rft.getTopmostOwner()).deep.equal({Substrate: alice.address});322 });291});323});292324293describe('Negative Test: Nesting', () => {325describe('Negative Test: Nesting', () => {