difftreelog
fix find_parent
in: master
8 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -155,6 +155,11 @@
}
impl<T: Config> CollectionHandle<T> {
+ /// Get the mode of the collection: NFT/FT/RFT.
+ pub fn mode(&self) -> CollectionMode {
+ self.mode
+ }
+
/// Same as [CollectionHandle::new] but with an explicit gas limit.
pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
<CollectionById<T>>::get(id).map(|collection| Self {
@@ -1872,6 +1877,9 @@
/// It wraps methods in Fungible, Nonfungible and Refungible pallets
/// and adds weight info.
pub trait CommonCollectionOperations<T: Config> {
+ /// Get the mode of the collection: NFT/FT/RFT.
+ fn mode(&self) -> CollectionMode;
+
/// Create token.
///
/// * `sender` - The user who mint the token and pays for the transaction.
pallets/fungible/src/common.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use core::marker::PhantomData;1819use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, traits::Get};20use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget, CreateItemData};21use pallet_common::{22 CommonCollectionOperations, CommonWeightInfo, RefungibleExtensions, with_weight,23 weights::WeightInfo as _,24};25use pallet_structure::Error as StructureError;26use sp_runtime::ArithmeticError;27use sp_std::{vec::Vec, vec};28use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};2930use crate::{31 Allowance, TotalSupply, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf,32 weights::WeightInfo,33};3435pub struct CommonWeights<T: Config>(PhantomData<T>);36impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {37 fn create_item() -> Weight {38 <SelfWeightOf<T>>::create_item()39 }4041 fn create_multiple_items(_data: &[CreateItemData]) -> Weight {42 // All items minted for the same user, so it works same as create_item43 Self::create_item()44 }4546 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {47 match data {48 CreateItemExData::Fungible(f) => {49 <SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)50 }51 _ => Weight::zero(),52 }53 }5455 fn burn_item() -> Weight {56 <SelfWeightOf<T>>::burn_item()57 }5859 fn set_collection_properties(amount: u32) -> Weight {60 <pallet_common::SelfWeightOf<T>>::set_collection_properties(amount)61 }6263 fn delete_collection_properties(amount: u32) -> Weight {64 <pallet_common::SelfWeightOf<T>>::delete_collection_properties(amount)65 }6667 fn set_token_properties(_amount: u32) -> Weight {68 // Error69 Weight::zero()70 }7172 fn delete_token_properties(_amount: u32) -> Weight {73 // Error74 Weight::zero()75 }7677 fn set_token_property_permissions(_amount: u32) -> Weight {78 // Error79 Weight::zero()80 }8182 fn transfer() -> Weight {83 <SelfWeightOf<T>>::transfer()84 }8586 fn approve() -> Weight {87 <SelfWeightOf<T>>::approve()88 }8990 fn approve_from() -> Weight {91 <SelfWeightOf<T>>::approve_from()92 }9394 fn transfer_from() -> Weight {95 <SelfWeightOf<T>>::transfer_from()96 }9798 fn burn_from() -> Weight {99 <SelfWeightOf<T>>::burn_from()100 }101102 fn burn_recursively_self_raw() -> Weight {103 // Read to get total balance104 Self::burn_item() + T::DbWeight::get().reads(1)105 }106107 fn burn_recursively_breadth_raw(_amount: u32) -> Weight {108 // Fungible tokens can't have children109 Weight::zero()110 }111112 fn token_owner() -> Weight {113 Weight::zero()114 }115116 fn set_allowance_for_all() -> Weight {117 Weight::zero()118 }119120 fn force_repair_item() -> Weight {121 Weight::zero()122 }123}124125/// Implementation of `CommonCollectionOperations` for `FungibleHandle`. It wraps FungibleHandle Pallete126/// methods and adds weight info.127impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {128 fn create_item(129 &self,130 sender: T::CrossAccountId,131 to: T::CrossAccountId,132 data: up_data_structs::CreateItemData,133 nesting_budget: &dyn Budget,134 ) -> DispatchResultWithPostInfo {135 match data {136 up_data_structs::CreateItemData::Fungible(data) => with_weight(137 <Pallet<T>>::create_item(self, &sender, (to, data.value), nesting_budget),138 <CommonWeights<T>>::create_item(),139 ),140 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),141 }142 }143144 fn create_multiple_items(145 &self,146 sender: T::CrossAccountId,147 to: T::CrossAccountId,148 data: Vec<up_data_structs::CreateItemData>,149 nesting_budget: &dyn Budget,150 ) -> DispatchResultWithPostInfo {151 let mut sum: u128 = 0;152 for data in data {153 match data {154 up_data_structs::CreateItemData::Fungible(data) => {155 sum = sum156 .checked_add(data.value)157 .ok_or(ArithmeticError::Overflow)?;158 }159 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),160 }161 }162163 with_weight(164 <Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),165 <CommonWeights<T>>::create_item(),166 )167 }168169 fn create_multiple_items_ex(170 &self,171 sender: <T>::CrossAccountId,172 data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,173 nesting_budget: &dyn Budget,174 ) -> DispatchResultWithPostInfo {175 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);176 let data = match data {177 up_data_structs::CreateItemExData::Fungible(f) => f,178 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),179 };180181 with_weight(182 <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),183 weight,184 )185 }186187 fn burn_item(188 &self,189 sender: T::CrossAccountId,190 token: TokenId,191 amount: u128,192 ) -> DispatchResultWithPostInfo {193 ensure!(194 token == TokenId::default(),195 <Error<T>>::FungibleItemsHaveNoId196 );197198 with_weight(199 <Pallet<T>>::burn(self, &sender, amount),200 <CommonWeights<T>>::burn_item(),201 )202 }203204 fn burn_item_recursively(205 &self,206 sender: T::CrossAccountId,207 token: TokenId,208 self_budget: &dyn Budget,209 _breadth_budget: &dyn Budget,210 ) -> DispatchResultWithPostInfo {211 // Should not happen?212 ensure!(213 token == TokenId::default(),214 <Error<T>>::FungibleItemsHaveNoId215 );216 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);217218 with_weight(219 <Pallet<T>>::burn(self, &sender, <Balance<T>>::get((self.id, &sender))),220 <CommonWeights<T>>::burn_recursively_self_raw(),221 )222 }223224 fn transfer(225 &self,226 from: T::CrossAccountId,227 to: T::CrossAccountId,228 token: TokenId,229 amount: u128,230 nesting_budget: &dyn Budget,231 ) -> DispatchResultWithPostInfo {232 ensure!(233 token == TokenId::default(),234 <Error<T>>::FungibleItemsHaveNoId235 );236237 with_weight(238 <Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget),239 <CommonWeights<T>>::transfer(),240 )241 }242243 fn approve(244 &self,245 sender: T::CrossAccountId,246 spender: T::CrossAccountId,247 token: TokenId,248 amount: u128,249 ) -> DispatchResultWithPostInfo {250 ensure!(251 token == TokenId::default(),252 <Error<T>>::FungibleItemsHaveNoId253 );254255 with_weight(256 <Pallet<T>>::set_allowance(self, &sender, &spender, amount),257 <CommonWeights<T>>::approve(),258 )259 }260261 fn approve_from(262 &self,263 sender: T::CrossAccountId,264 from: T::CrossAccountId,265 to: T::CrossAccountId,266 token: TokenId,267 amount: u128,268 ) -> DispatchResultWithPostInfo {269 ensure!(270 token == TokenId::default(),271 <Error<T>>::FungibleItemsHaveNoId272 );273274 with_weight(275 <Pallet<T>>::set_allowance_from(self, &sender, &from, &to, amount),276 <CommonWeights<T>>::approve_from(),277 )278 }279280 fn transfer_from(281 &self,282 sender: T::CrossAccountId,283 from: T::CrossAccountId,284 to: T::CrossAccountId,285 token: TokenId,286 amount: u128,287 nesting_budget: &dyn Budget,288 ) -> DispatchResultWithPostInfo {289 ensure!(290 token == TokenId::default(),291 <Error<T>>::FungibleItemsHaveNoId292 );293294 with_weight(295 <Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget),296 <CommonWeights<T>>::transfer_from(),297 )298 }299300 fn burn_from(301 &self,302 sender: T::CrossAccountId,303 from: T::CrossAccountId,304 token: TokenId,305 amount: u128,306 nesting_budget: &dyn Budget,307 ) -> DispatchResultWithPostInfo {308 ensure!(309 token == TokenId::default(),310 <Error<T>>::FungibleItemsHaveNoId311 );312313 with_weight(314 <Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),315 <CommonWeights<T>>::burn_from(),316 )317 }318319 fn set_collection_properties(320 &self,321 sender: T::CrossAccountId,322 properties: Vec<Property>,323 ) -> DispatchResultWithPostInfo {324 let weight = <CommonWeights<T>>::set_collection_properties(properties.len() as u32);325326 with_weight(327 <Pallet<T>>::set_collection_properties(self, &sender, properties),328 weight,329 )330 }331332 fn delete_collection_properties(333 &self,334 sender: &T::CrossAccountId,335 property_keys: Vec<PropertyKey>,336 ) -> DispatchResultWithPostInfo {337 let weight = <CommonWeights<T>>::delete_collection_properties(property_keys.len() as u32);338339 with_weight(340 <Pallet<T>>::delete_collection_properties(self, sender, property_keys),341 weight,342 )343 }344345 fn set_token_properties(346 &self,347 _sender: T::CrossAccountId,348 _token_id: TokenId,349 _property: Vec<Property>,350 _nesting_budget: &dyn Budget,351 ) -> DispatchResultWithPostInfo {352 fail!(<Error<T>>::SettingPropertiesNotAllowed)353 }354355 fn set_token_property_permissions(356 &self,357 _sender: &T::CrossAccountId,358 _property_permissions: Vec<PropertyKeyPermission>,359 ) -> DispatchResultWithPostInfo {360 fail!(<Error<T>>::SettingPropertiesNotAllowed)361 }362363 fn delete_token_properties(364 &self,365 _sender: T::CrossAccountId,366 _token_id: TokenId,367 _property_keys: Vec<PropertyKey>,368 _nesting_budget: &dyn Budget,369 ) -> DispatchResultWithPostInfo {370 fail!(<Error<T>>::SettingPropertiesNotAllowed)371 }372373 fn check_nesting(374 &self,375 _sender: <T>::CrossAccountId,376 _from: (CollectionId, TokenId),377 _under: TokenId,378 _nesting_budget: &dyn Budget,379 ) -> sp_runtime::DispatchResult {380 fail!(<Error<T>>::FungibleDisallowsNesting)381 }382383 fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}384385 fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}386387 fn collection_tokens(&self) -> Vec<TokenId> {388 vec![TokenId::default()]389 }390391 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {392 if <Balance<T>>::get((self.id, account)) != 0 {393 vec![TokenId::default()]394 } else {395 vec![]396 }397 }398399 fn token_exists(&self, token: TokenId) -> bool {400 token == TokenId::default()401 }402403 fn last_token_id(&self) -> TokenId {404 TokenId::default()405 }406407 fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {408 None409 }410411 /// Returns 10 tokens owners in no particular order.412 fn token_owners(&self, token: TokenId) -> Vec<T::CrossAccountId> {413 <Pallet<T>>::token_owners(self.id, token).unwrap_or_default()414 }415416 fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {417 None418 }419420 fn token_properties(421 &self,422 _token_id: TokenId,423 _keys: Option<Vec<PropertyKey>>,424 ) -> Vec<Property> {425 Vec::new()426 }427428 fn total_supply(&self) -> u32 {429 1430 }431432 fn account_balance(&self, account: T::CrossAccountId) -> u32 {433 if <Balance<T>>::get((self.id, account)) != 0 {434 1435 } else {436 0437 }438 }439440 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {441 if token != TokenId::default() {442 return 0;443 }444 <Balance<T>>::get((self.id, account))445 }446447 fn allowance(448 &self,449 sender: T::CrossAccountId,450 spender: T::CrossAccountId,451 token: TokenId,452 ) -> u128 {453 if token != TokenId::default() {454 return 0;455 }456 <Allowance<T>>::get((self.id, sender, spender))457 }458459 fn refungible_extensions(&self) -> Option<&dyn RefungibleExtensions<T>> {460 None461 }462463 fn total_pieces(&self, token: TokenId) -> Option<u128> {464 if token != TokenId::default() {465 return None;466 }467 <TotalSupply<T>>::try_get(self.id).ok()468 }469470 fn set_allowance_for_all(471 &self,472 _owner: T::CrossAccountId,473 _operator: T::CrossAccountId,474 _approve: bool,475 ) -> DispatchResultWithPostInfo {476 fail!(<Error<T>>::SettingAllowanceForAllNotAllowed)477 }478479 fn allowance_for_all(&self, _owner: T::CrossAccountId, _operator: T::CrossAccountId) -> bool {480 false481 }482483 /// Repairs a possibly broken item.484 fn repair_item(&self, _token: TokenId) -> DispatchResultWithPostInfo {485 fail!(<Error<T>>::FungibleTokensAreAlwaysValid)486 }487}pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -152,6 +152,10 @@
/// Implementation of `CommonCollectionOperations` for `NonfungibleHandle`. It wraps Nonfungible Pallete
/// methods and adds weight info.
impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {
+ fn mode(&self) -> up_data_structs::CollectionMode {
+ self.0.mode()
+ }
+
fn create_item(
&self,
sender: T::CrossAccountId,
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -741,7 +741,8 @@
Some((collection_id, nft_id)),
&target_nft_budget,
)
- .map_err(Self::map_unique_err_to_proxy)?;
+ .map_err(Self::map_unique_err_to_proxy)?
+ .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
approval_required = cross_sender != target_nft_owner;
@@ -989,7 +990,8 @@
let nft_owner =
<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
- .map_err(|_| <Error<T>>::ResourceDoesntExist)?;
+ .map_err(|_| <Error<T>>::ResourceDoesntExist)?
+ .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
Self::try_mutate_resource_info(collection_id, nft_id, resource_id, |res| {
ensure!(res.pending, <Error<T>>::ResourceNotPending);
@@ -1044,7 +1046,8 @@
let nft_owner =
<PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
- .map_err(|_| <Error<T>>::ResourceDoesntExist)?;
+ .map_err(|_| <Error<T>>::ResourceDoesntExist)?
+ .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
ensure!(cross_sender == nft_owner, <Error<T>>::NoPermission);
@@ -1666,7 +1669,8 @@
let budget = budget::Value::new(NESTING_BUDGET);
let nft_owner = <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)
- .map_err(Self::map_unique_err_to_proxy)?;
+ .map_err(Self::map_unique_err_to_proxy)?
+ .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
let pending = sender != nft_owner;
@@ -1720,7 +1724,8 @@
let budget = up_data_structs::budget::Value::new(NESTING_BUDGET);
let topmost_owner =
- <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?;
+ <PalletStructure<T>>::find_topmost_owner(collection_id, nft_id, &budget)?
+ .ok_or::<DispatchError>(<Error<T>>::NoPermission.into())?;
let sender = T::CrossAccountId::from_sub(sender);
if topmost_owner == sender {
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -186,6 +186,10 @@
/// Implementation of `CommonCollectionOperations` for `RefungibleHandle`. It wraps Refungible Pallete
/// methods and adds weight info.
impl<T: Config> CommonCollectionOperations<T> for RefungibleHandle<T> {
+ fn mode(&self) -> up_data_structs::CollectionMode {
+ self.0.mode()
+ }
+
fn create_item(
&self,
sender: T::CrossAccountId,
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -61,6 +61,7 @@
use frame_support::fail;
pub use pallet::*;
use pallet_common::{dispatch::CollectionDispatch, CollectionHandle};
+use up_data_structs::CollectionMode;
use up_data_structs::{CollectionId, TokenId, mapping::TokenAddressMapping, budget::Budget};
#[cfg(feature = "runtime-benchmarks")]
@@ -135,6 +136,8 @@
User(CrossAccountId),
/// Could not find the token provided as the owner.
TokenNotFound,
+ /// Nested token has multiple owners.
+ MultipleOwners,
/// Token owner is another token (still, the target token may not exist).
Token(CollectionId, TokenId),
}
@@ -163,6 +166,10 @@
Some((collection, token)) => Parent::Token(collection, token),
None => Parent::User(owner),
},
+ None if handle.mode() == CollectionMode::ReFungible => handle
+ .total_pieces(token)
+ .map(|_| Parent::MultipleOwners)
+ .unwrap_or(Parent::TokenNotFound),
None => Parent::TokenNotFound,
})
}
@@ -203,19 +210,27 @@
///
/// May return token address if parent token not yet exists
///
+ /// Returns `None` if the token has multiple owners.
+ ///
/// - `budget`: Limit for searching parents in depth.
pub fn find_topmost_owner(
collection: CollectionId,
token: TokenId,
budget: &dyn Budget,
- ) -> Result<T::CrossAccountId, DispatchError> {
+ ) -> Result<Option<T::CrossAccountId>, DispatchError> {
let owner = Self::parent_chain(collection, token)
.take_while(|_| budget.consume())
- .find(|p| matches!(p, Ok(Parent::User(_) | Parent::TokenNotFound)))
+ .find(|p| {
+ matches!(
+ p,
+ Ok(Parent::User(_) | Parent::TokenNotFound | Parent::MultipleOwners)
+ )
+ })
.ok_or(<Error<T>>::DepthLimit)??;
Ok(match owner {
- Parent::User(v) => v,
+ Parent::User(v) => Some(v),
+ Parent::MultipleOwners => None,
_ => fail!(<Error<T>>::TokenNotFound),
})
}
@@ -223,13 +238,15 @@
/// Find the topmost parent and check that assigning `for_nest` token as a child for
/// `token` wouldn't create a cycle.
///
+ /// Returns `None` if the token has multiple owners.
+ ///
/// - `budget`: Limit for searching parents in depth.
pub fn get_checked_topmost_owner(
collection: CollectionId,
token: TokenId,
for_nest: Option<(CollectionId, TokenId)>,
budget: &dyn Budget,
- ) -> Result<T::CrossAccountId, DispatchError> {
+ ) -> Result<Option<T::CrossAccountId>, DispatchError> {
// Tried to nest token in itself
if Some((collection, token)) == for_nest {
return Err(<Error<T>>::OuroborosDetected.into());
@@ -242,8 +259,9 @@
return Err(<Error<T>>::OuroborosDetected.into())
}
// Token is owned by other user
- Parent::User(user) => return Ok(user),
+ Parent::User(user) => return Ok(Some(user)),
Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
+ Parent::MultipleOwners => return Ok(None),
// Continue parent chain
Parent::Token(_, _) => {}
}
@@ -284,12 +302,17 @@
budget: &dyn Budget,
) -> Result<bool, DispatchError> {
let target_parent = match T::CrossTokenAddressMapping::address_to_token(&user) {
- Some((collection, token)) => Self::find_topmost_owner(collection, token, budget)?,
+ Some((collection, token)) => match Self::find_topmost_owner(collection, token, budget)?
+ {
+ Some(topmost_owner) => topmost_owner,
+ None => return Ok(false),
+ },
None => user,
};
- Self::get_checked_topmost_owner(collection, token, for_nest, budget)
- .map(|indirect_owner| indirect_owner == target_parent)
+ Self::get_checked_topmost_owner(collection, token, for_nest, budget).map(|indirect_owner| {
+ indirect_owner.map_or(false, |indirect_owner| indirect_owner == target_parent)
+ })
}
/// Checks that `under` is valid token and that `token_id` could be nested under it
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -252,7 +252,7 @@
/// Collection can represent various types of tokens.
/// Each collection can contain only one type of tokens at a time.
/// This type helps to understand which tokens the collection contains.
-#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq, TypeInfo, MaxEncodedLen)]
+#[derive(Encode, Decode, Eq, Debug, Clone, Copy, PartialEq, TypeInfo, MaxEncodedLen)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
pub enum CollectionMode {
/// Non fungible tokens.
runtime/common/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/runtime_apis.rs
+++ b/runtime/common/runtime_apis.rs
@@ -83,7 +83,7 @@
fn topmost_token_owner(collection: CollectionId, token: TokenId) -> Result<Option<CrossAccountId>, DispatchError> {
let budget = up_data_structs::budget::Value::new(10);
- Ok(Some(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?))
+ Ok(<pallet_structure::Pallet<Runtime>>::find_topmost_owner(collection, token, &budget)?)
}
fn token_children(collection: CollectionId, token: TokenId) -> Result<Vec<TokenChild>, DispatchError> {
Ok(<pallet_nonfungible::Pallet<Runtime>>::token_children_ids(collection, token))