difftreelog
feat initial rmrk send impl
in: master
10 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -1298,6 +1298,7 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo;
fn transfer_from(
&self,
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::{CommonCollectionOperations, CommonWeightInfo, with_weight};22use pallet_structure::Error as StructureError;23use sp_runtime::ArithmeticError;24use sp_std::{vec::Vec, vec};25use up_data_structs::{Property, PropertyKey, PropertyValue, PropertyKeyPermission};2627use crate::{28 Allowance, Balance, Config, Error, FungibleHandle, Pallet, SelfWeightOf, weights::WeightInfo,29};3031pub struct CommonWeights<T: Config>(PhantomData<T>);32impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {33 fn create_item() -> Weight {34 <SelfWeightOf<T>>::create_item()35 }3637 fn create_multiple_items(_data: &[CreateItemData]) -> Weight {38 // All items minted for the same user, so it works same as create_item39 Self::create_item()40 }4142 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {43 match data {44 CreateItemExData::Fungible(f) => {45 <SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)46 }47 _ => 0,48 }49 }5051 fn burn_item() -> Weight {52 <SelfWeightOf<T>>::burn_item()53 }5455 fn set_collection_properties(_amount: u32) -> Weight {56 // Error57 058 }5960 fn delete_collection_properties(_amount: u32) -> Weight {61 // Error62 063 }6465 fn set_token_properties(_amount: u32) -> Weight {66 // Error67 068 }6970 fn delete_token_properties(_amount: u32) -> Weight {71 // Error72 073 }7475 fn set_property_permissions(_amount: u32) -> Weight {76 // Error77 078 }7980 fn transfer() -> Weight {81 <SelfWeightOf<T>>::transfer()82 }8384 fn approve() -> Weight {85 <SelfWeightOf<T>>::approve()86 }8788 fn transfer_from() -> Weight {89 <SelfWeightOf<T>>::transfer_from()90 }9192 fn burn_from() -> Weight {93 <SelfWeightOf<T>>::burn_from()94 }9596 fn burn_recursively_self_raw() -> Weight {97 // Read to get total balance98 Self::burn_item() + T::DbWeight::get().reads(1)99 }100101 fn burn_recursively_breadth_raw(_amount: u32) -> Weight {102 // Fungible tokens can't have children103 0104 }105}106107impl<T: Config> CommonCollectionOperations<T> for FungibleHandle<T> {108 fn create_item(109 &self,110 sender: T::CrossAccountId,111 to: T::CrossAccountId,112 data: up_data_structs::CreateItemData,113 nesting_budget: &dyn Budget,114 ) -> DispatchResultWithPostInfo {115 match data {116 up_data_structs::CreateItemData::Fungible(data) => with_weight(117 <Pallet<T>>::create_item(self, &sender, (to, data.value), nesting_budget),118 <CommonWeights<T>>::create_item(),119 ),120 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),121 }122 }123124 fn create_multiple_items(125 &self,126 sender: T::CrossAccountId,127 to: T::CrossAccountId,128 data: Vec<up_data_structs::CreateItemData>,129 nesting_budget: &dyn Budget,130 ) -> DispatchResultWithPostInfo {131 let mut sum: u128 = 0;132 for data in data {133 match data {134 up_data_structs::CreateItemData::Fungible(data) => {135 sum = sum136 .checked_add(data.value)137 .ok_or(ArithmeticError::Overflow)?;138 }139 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),140 }141 }142143 with_weight(144 <Pallet<T>>::create_item(self, &sender, (to, sum), nesting_budget),145 <CommonWeights<T>>::create_item(),146 )147 }148149 fn create_multiple_items_ex(150 &self,151 sender: <T>::CrossAccountId,152 data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,153 nesting_budget: &dyn Budget,154 ) -> DispatchResultWithPostInfo {155 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);156 let data = match data {157 up_data_structs::CreateItemExData::Fungible(f) => f,158 _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),159 };160161 with_weight(162 <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner(), nesting_budget),163 weight,164 )165 }166167 fn burn_item(168 &self,169 sender: T::CrossAccountId,170 token: TokenId,171 amount: u128,172 ) -> DispatchResultWithPostInfo {173 ensure!(174 token == TokenId::default(),175 <Error<T>>::FungibleItemsHaveNoId176 );177178 with_weight(179 <Pallet<T>>::burn(self, &sender, amount),180 <CommonWeights<T>>::burn_item(),181 )182 }183184 fn burn_item_recursively(185 &self,186 sender: T::CrossAccountId,187 token: TokenId,188 self_budget: &dyn Budget,189 _breadth_budget: &dyn Budget,190 ) -> DispatchResultWithPostInfo {191 // Should not happen?192 ensure!(193 token == TokenId::default(),194 <Error<T>>::FungibleItemsHaveNoId195 );196 ensure!(self_budget.consume(), <StructureError<T>>::DepthLimit,);197198 with_weight(199 <Pallet<T>>::burn(self, &sender, <Balance<T>>::get((self.id, &sender))),200 <CommonWeights<T>>::burn_recursively_self_raw(),201 )202 }203204 fn transfer(205 &self,206 from: T::CrossAccountId,207 to: T::CrossAccountId,208 token: TokenId,209 amount: u128,210 nesting_budget: &dyn Budget,211 ) -> DispatchResultWithPostInfo {212 ensure!(213 token == TokenId::default(),214 <Error<T>>::FungibleItemsHaveNoId215 );216217 with_weight(218 <Pallet<T>>::transfer(self, &from, &to, amount, nesting_budget),219 <CommonWeights<T>>::transfer(),220 )221 }222223 fn approve(224 &self,225 sender: T::CrossAccountId,226 spender: T::CrossAccountId,227 token: TokenId,228 amount: u128,229 ) -> DispatchResultWithPostInfo {230 ensure!(231 token == TokenId::default(),232 <Error<T>>::FungibleItemsHaveNoId233 );234235 with_weight(236 <Pallet<T>>::set_allowance(self, &sender, &spender, amount),237 <CommonWeights<T>>::approve(),238 )239 }240241 fn transfer_from(242 &self,243 sender: T::CrossAccountId,244 from: T::CrossAccountId,245 to: T::CrossAccountId,246 token: TokenId,247 amount: u128,248 nesting_budget: &dyn Budget,249 ) -> DispatchResultWithPostInfo {250 ensure!(251 token == TokenId::default(),252 <Error<T>>::FungibleItemsHaveNoId253 );254255 with_weight(256 <Pallet<T>>::transfer_from(self, &sender, &from, &to, amount, nesting_budget),257 <CommonWeights<T>>::transfer_from(),258 )259 }260261 fn burn_from(262 &self,263 sender: T::CrossAccountId,264 from: T::CrossAccountId,265 token: TokenId,266 amount: u128,267 nesting_budget: &dyn Budget,268 ) -> DispatchResultWithPostInfo {269 ensure!(270 token == TokenId::default(),271 <Error<T>>::FungibleItemsHaveNoId272 );273274 with_weight(275 <Pallet<T>>::burn_from(self, &sender, &from, amount, nesting_budget),276 <CommonWeights<T>>::burn_from(),277 )278 }279280 fn set_collection_properties(281 &self,282 _sender: T::CrossAccountId,283 _property: Vec<Property>,284 ) -> DispatchResultWithPostInfo {285 fail!(<Error<T>>::SettingPropertiesNotAllowed)286 }287288 fn delete_collection_properties(289 &self,290 _sender: &T::CrossAccountId,291 _property_keys: Vec<PropertyKey>,292 ) -> DispatchResultWithPostInfo {293 fail!(<Error<T>>::SettingPropertiesNotAllowed)294 }295296 fn set_token_properties(297 &self,298 _sender: T::CrossAccountId,299 _token_id: TokenId,300 _property: Vec<Property>,301 ) -> DispatchResultWithPostInfo {302 fail!(<Error<T>>::SettingPropertiesNotAllowed)303 }304305 fn set_property_permissions(306 &self,307 _sender: &T::CrossAccountId,308 _property_permissions: Vec<PropertyKeyPermission>,309 ) -> DispatchResultWithPostInfo {310 fail!(<Error<T>>::SettingPropertiesNotAllowed)311 }312313 fn delete_token_properties(314 &self,315 _sender: T::CrossAccountId,316 _token_id: TokenId,317 _property_keys: Vec<PropertyKey>,318 ) -> DispatchResultWithPostInfo {319 fail!(<Error<T>>::SettingPropertiesNotAllowed)320 }321322 fn check_nesting(323 &self,324 _sender: <T>::CrossAccountId,325 _from: (CollectionId, TokenId),326 _under: TokenId,327 _budget: &dyn Budget,328 ) -> sp_runtime::DispatchResult {329 fail!(<Error<T>>::FungibleDisallowsNesting)330 }331332 fn nest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}333334 fn unnest(&self, _under: TokenId, _to_nest: (CollectionId, TokenId)) {}335336 fn collection_tokens(&self) -> Vec<TokenId> {337 vec![TokenId::default()]338 }339340 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {341 if <Balance<T>>::get((self.id, account)) != 0 {342 vec![TokenId::default()]343 } else {344 vec![]345 }346 }347348 fn token_exists(&self, token: TokenId) -> bool {349 token == TokenId::default()350 }351352 fn last_token_id(&self) -> TokenId {353 TokenId::default()354 }355356 fn token_owner(&self, _token: TokenId) -> Option<T::CrossAccountId> {357 None358 }359360 fn token_property(&self, _token_id: TokenId, _key: &PropertyKey) -> Option<PropertyValue> {361 None362 }363364 fn token_properties(365 &self,366 _token_id: TokenId,367 _keys: Option<Vec<PropertyKey>>,368 ) -> Vec<Property> {369 Vec::new()370 }371372 fn total_supply(&self) -> u32 {373 1374 }375376 fn account_balance(&self, account: T::CrossAccountId) -> u32 {377 if <Balance<T>>::get((self.id, account)) != 0 {378 1379 } else {380 0381 }382 }383384 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {385 if token != TokenId::default() {386 return 0;387 }388 <Balance<T>>::get((self.id, account))389 }390391 fn allowance(392 &self,393 sender: T::CrossAccountId,394 spender: T::CrossAccountId,395 token: TokenId,396 ) -> u128 {397 if token != TokenId::default() {398 return 0;399 }400 <Allowance<T>>::get((self.id, sender, spender))401 }402}pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -308,14 +308,15 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);
with_weight(
if amount == 1 {
- <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))
+ <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender), nesting_budget)
} else {
- <Pallet<T>>::set_allowance(self, &sender, token, None)
+ <Pallet<T>>::set_allowance(self, &sender, token, None, nesting_budget)
},
<CommonWeights<T>>::approve(),
)
pallets/nonfungible/src/erc.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/erc.rs
+++ b/pallets/nonfungible/src/erc.rs
@@ -269,8 +269,11 @@
let caller = T::CrossAccountId::from_eth(caller);
let approved = T::CrossAccountId::from_eth(approved);
let token = token_id.try_into()?;
+ let budget = self
+ .recorder
+ .weight_calls_budget(<StructureWeight<T>>::find_parent());
- <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved))
+ <Pallet<T>>::set_allowance(self, &caller, token, Some(&approved), &budget)
.map_err(dispatch_to_evm::<T>)?;
Ok(())
}
pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -878,6 +878,7 @@
sender: &T::CrossAccountId,
token: TokenId,
spender: Option<&T::CrossAccountId>,
+ nesting_budget: &dyn Budget,
) -> DispatchResult {
if collection.permissions.access() == AccessMode::AllowList {
collection.check_allowlist(sender)?;
@@ -889,9 +890,16 @@
if let Some(spender) = spender {
<PalletCommon<T>>::ensure_correct_receiver(spender)?;
}
- let token_data =
- <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;
- if &token_data.owner != sender {
+
+ let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+ sender.clone(),
+ collection.id,
+ token,
+ None,
+ nesting_budget
+ )?;
+
+ if !is_owned {
ensure!(
collection.ignores_owned_amount(sender),
<CommonError<T>>::CantApproveMoreThanOwned
@@ -918,6 +926,9 @@
// `from`, `to` checked in [`transfer`]
collection.check_allowlist(spender)?;
}
+ if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
+ return Ok(());
+ }
if let Some(source) = T::CrossTokenAddressMapping::address_to_token(from) {
// TODO: should collection owner be allowed to perform this transfer?
ensure!(
@@ -930,9 +941,6 @@
)?,
<CommonError<T>>::ApprovedValueTooLow,
);
- return Ok(());
- }
- if <Allowance<T>>::get((collection.id, token)).as_ref() == Some(spender) {
return Ok(());
}
ensure!(
pallets/proxy-rmrk-core/src/lib.rsdiffbeforeafterboth--- a/pallets/proxy-rmrk-core/src/lib.rs
+++ b/pallets/proxy-rmrk-core/src/lib.rs
@@ -20,7 +20,7 @@
use frame_system::{pallet_prelude::*, ensure_signed};
use sp_runtime::{DispatchError, Permill, traits::StaticLookup};
use sp_std::vec::Vec;
-use up_data_structs::*;
+use up_data_structs::{*, mapping::TokenAddressMapping};
use pallet_common::{
Pallet as PalletCommon, Error as CommonError, CollectionHandle, CommonCollectionOperations,
};
@@ -39,6 +39,8 @@
use RmrkProperty::*;
+const NESTING_BUDGET: u32 = 5;
+
#[frame_support::pallet]
pub mod pallet {
use super::*;
@@ -56,10 +58,13 @@
pub type CollectionIndex<T: Config> = StorageValue<_, RmrkCollectionId, ValueQuery>;
#[pallet::storage]
- #[pallet::getter(fn collection_index_map)]
- pub type CollectionIndexMap<T: Config> =
+ pub type UniqueCollectionId<T: Config> =
StorageMap<_, Twox64Concat, RmrkCollectionId, CollectionId, ValueQuery>;
+ #[pallet::storage]
+ pub type RmrkInernalCollectionId<T: Config> =
+ StorageMap<_, Twox64Concat, CollectionId, RmrkCollectionId, ValueQuery>;
+
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
pub struct Pallet<T>(_);
@@ -151,10 +156,12 @@
.into_inner()
.try_into()
.map_err(|_| <CommonError<T>>::CollectionTokenPrefixLimitExceeded)?,
+ permissions: Some(CollectionPermissions {
+ nesting: Some(NestingRule::Owner),
+ ..Default::default()
+ }),
..Default::default()
};
-
- <CollectionIndex<T>>::mutate(|n| *n += 1);
let unique_collection_id = Self::init_collection(
T::CrossAccountId::from_sub(sender.clone()),
@@ -167,7 +174,10 @@
)?;
let rmrk_collection_id = <CollectionIndex<T>>::get();
- <CollectionIndexMap<T>>::insert(rmrk_collection_id, unique_collection_id);
+ <UniqueCollectionId<T>>::insert(rmrk_collection_id, unique_collection_id);
+ <RmrkInernalCollectionId<T>>::insert(unique_collection_id, rmrk_collection_id);
+
+ <CollectionIndex<T>>::mutate(|n| *n += 1);
Self::deposit_event(Event::CollectionCreated {
issuer: sender,
@@ -356,6 +366,81 @@
#[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
#[transactional]
+ pub fn send(
+ origin: OriginFor<T>,
+ rmrk_collection_id: RmrkCollectionId,
+ rmrk_nft_id: RmrkNftId,
+ new_owner: RmrkAccountIdOrCollectionNftTuple<T::AccountId>,
+ ) -> DispatchResult {
+ let sender = ensure_signed(origin.clone())?;
+ let cross_sender = T::CrossAccountId::from_sub(sender.clone());
+
+ let collection_id = Self::unique_collection_id(rmrk_collection_id)?;
+ let nft_id = rmrk_nft_id.into();
+
+ let token_data = <TokenData<T>>::get((collection_id, nft_id))
+ .ok_or(<Error<T>>::NoAvailableNftId)?;
+
+ let from = token_data.owner;
+
+ let collection = Self::get_typed_nft_collection(
+ collection_id,
+ misc::CollectionType::Regular,
+ )?;
+
+ let budget = budget::Value::new(NESTING_BUDGET);
+
+ let target_owner;
+
+ match new_owner {
+ RmrkAccountIdOrCollectionNftTuple::AccountId(account_id) => {
+ target_owner = T::CrossAccountId::from_sub(account_id);
+ },
+ RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(target_collection_id, target_nft_id) => {
+ let target_collection_id = Self::unique_collection_id(target_collection_id)?;
+
+ target_owner = T::CrossTokenAddressMapping::token_to_address(
+ target_collection_id,
+ target_nft_id.into(),
+ );
+
+ let spender = <PalletStructure<T>>::get_indirect_owner(
+ target_collection_id,
+ target_nft_id.into(),
+ Some((collection_id, nft_id)),
+ &budget,
+ )?;
+
+ let is_approval_required = cross_sender != spender;
+
+ if is_approval_required {
+ <PalletNft<T>>::set_allowance(
+ &collection,
+ &cross_sender,
+ nft_id,
+ Some(&spender),
+ &budget
+ ).map_err(Self::map_common_err_to_proxy)?;
+
+ return Ok(());
+ }
+ }
+ }
+
+ <PalletNft<T>>::transfer_from(
+ &collection,
+ &cross_sender,
+ &from,
+ &target_owner,
+ nft_id,
+ &budget
+ ).map_err(Self::map_common_err_to_proxy)?;
+
+ Ok(())
+ }
+
+ #[pallet::weight(10_000 + T::DbWeight::get().reads_writes(1,1))]
+ #[transactional]
pub fn set_property(
origin: OriginFor<T>,
#[pallet::compact] rmrk_collection_id: RmrkCollectionId,
@@ -367,12 +452,13 @@
let sender = T::CrossAccountId::from_sub(sender);
let collection_id = Self::unique_collection_id(rmrk_collection_id)?;
+ let budget = budget::Value::new(NESTING_BUDGET);
match maybe_nft_id {
Some(nft_id) => {
let token_id: TokenId = nft_id.into();
- Self::ensure_nft_owner(collection_id, token_id, &sender)?;
+ Self::ensure_nft_owner(collection_id, token_id, &sender, &budget)?;
Self::ensure_nft_type(collection_id, token_id, NftType::Regular)?;
<PalletNft<T>>::set_scoped_token_property(
@@ -607,7 +693,7 @@
owner: owner.clone(),
};
- let budget = budget::Value::new(2);
+ let budget = budget::Value::new(NESTING_BUDGET);
<PalletNft<T>>::create_item(collection, sender, data, &budget)?;
@@ -651,14 +737,8 @@
//ensure!(!Pallet::<T>::is_locked(collection_id, nft_id), pallet_uniques::Error::<T>::Locked);
let sender = T::CrossAccountId::from_sub(sender);
- let budget = budget::Value::new(10);
- let pending = !<PalletStructure<T>>::check_indirectly_owned(
- sender.clone(),
- collection_id,
- token_id,
- None,
- &budget,
- )?;
+ let budget = budget::Value::new(NESTING_BUDGET);
+ let pending = Self::ensure_nft_owner(collection_id, token_id, &sender, &budget).is_err();
let resource_collection_id: CollectionId =
Self::get_nft_property_decoded(collection_id, token_id, ResourceCollection)?;
@@ -757,7 +837,14 @@
pub fn unique_collection_id(
rmrk_collection_id: RmrkCollectionId,
) -> Result<CollectionId, DispatchError> {
- <CollectionIndexMap<T>>::try_get(rmrk_collection_id)
+ <UniqueCollectionId<T>>::try_get(rmrk_collection_id)
+ .map_err(|_| <Error<T>>::CollectionUnknown.into())
+ }
+
+ pub fn rmrk_collection_id(
+ unique_collection_id: CollectionId
+ ) -> Result<RmrkCollectionId, DispatchError> {
+ <RmrkInernalCollectionId<T>>::try_get(unique_collection_id)
.map_err(|_| <Error<T>>::CollectionUnknown.into())
}
@@ -873,12 +960,18 @@
collection_id: CollectionId,
token_id: TokenId,
possible_owner: &T::CrossAccountId,
+ nesting_budget: &dyn budget::Budget
) -> DispatchResult {
- let token_data =
- <TokenData<T>>::get((collection_id, token_id)).ok_or(<Error<T>>::NoAvailableNftId)?;
+ let is_owned = <PalletStructure<T>>::check_indirectly_owned(
+ possible_owner.clone(),
+ collection_id,
+ token_id,
+ None,
+ nesting_budget,
+ )?;
ensure!(
- token_data.owner == *possible_owner,
+ is_owned,
<Error<T>>::NoPermission
);
@@ -965,7 +1058,8 @@
NoPermission => NoPermission,
CollectionTokenLimitExceeded => CollectionFullOrLocked,
PublicMintingNotAllowed => NoPermission,
- TokenNotFound => NoAvailableNftId
+ TokenNotFound => NoAvailableNftId,
+ ApprovedValueTooLow => NoPermission
}
}
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -254,6 +254,7 @@
spender: T::CrossAccountId,
token: TokenId,
amount: u128,
+ _nesting_budget: &dyn Budget,
) -> DispatchResultWithPostInfo {
with_weight(
<Pallet<T>>::set_allowance(self, &sender, &spender, token, amount),
pallets/structure/src/lib.rsdiffbeforeafterboth--- a/pallets/structure/src/lib.rs
+++ b/pallets/structure/src/lib.rs
@@ -149,19 +149,12 @@
})
}
- /// Check if token indirectly owned by specified user
- pub fn check_indirectly_owned(
- user: T::CrossAccountId,
+ pub fn get_indirect_owner(
collection: CollectionId,
token: TokenId,
for_nest: Option<(CollectionId, TokenId)>,
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)?,
- None => user,
- };
-
+ ) -> Result<T::CrossAccountId, DispatchError> {
// Tried to nest token in itself
if Some((collection, token)) == for_nest {
return Err(<Error<T>>::OuroborosDetected.into());
@@ -173,10 +166,8 @@
Parent::Token(collection, token) if Some((collection, token)) == for_nest => {
return Err(<Error<T>>::OuroborosDetected.into())
}
- // Found needed parent, token is indirecty owned
- Parent::User(user) if user == target_parent => return Ok(true),
// Token is owned by other user
- Parent::User(_) => return Ok(false),
+ Parent::User(user) => return Ok(user),
Parent::TokenNotFound => return Err(<Error<T>>::TokenNotFound.into()),
// Continue parent chain
Parent::Token(_, _) => {}
@@ -199,6 +190,27 @@
dispatch.burn_item_recursively(from.clone(), token, self_budget, breadth_budget)
}
+ /// Check if token indirectly owned by specified user
+ pub fn check_indirectly_owned(
+ user: T::CrossAccountId,
+ collection: CollectionId,
+ token: TokenId,
+ for_nest: Option<(CollectionId, TokenId)>,
+ 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)?,
+ None => user,
+ };
+
+ Self::get_indirect_owner(
+ collection,
+ token,
+ for_nest,
+ budget
+ ).map(|indirect_owner| indirect_owner == target_parent)
+ }
+
pub fn check_nesting(
from: T::CrossAccountId,
under: &T::CrossAccountId,
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -815,8 +815,9 @@
#[transactional]
pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {
let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+ let budget = budget::Value::new(NESTING_BUDGET);
- dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))
+ dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount, &budget))
}
/// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.
runtime/common/src/runtime_apis.rsdiffbeforeafterboth--- a/runtime/common/src/runtime_apis.rs
+++ b/runtime/common/src/runtime_apis.rs
@@ -147,7 +147,11 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
+
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -169,7 +173,10 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -180,7 +187,11 @@
let owner = match collection.token_owner(nft_id) {
Some(owner) => match <Runtime as pallet_common::Config>::CrossTokenAddressMapping::address_to_token(&owner) {
- Some((col, tok)) => RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(col.0, tok.0),
+ Some((col, tok)) => {
+ let rmrk_collection = RmrkCore::rmrk_collection_id(col)?;
+
+ RmrkAccountIdOrCollectionNftTuple::CollectionAndNftTuple(rmrk_collection, tok.0)
+ }
None => RmrkAccountIdOrCollectionNftTuple::AccountId(owner.as_sub().clone())
},
None => return Ok(None)
@@ -202,11 +213,11 @@
use pallet_common::CommonCollectionOperations;
let cross_account_id = CrossAccountId::from_sub(account_id);
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Regular) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
Ok(
collection.account_tokens(cross_account_id)
@@ -217,28 +228,35 @@
}
fn nft_children(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkNftChild>, DispatchError> {
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
let nft_id = TokenId(nft_id);
if !RmrkCore::nft_exists(collection_id, nft_id) { return Ok(Vec::new()); }
Ok(
pallet_nonfungible::TokenChildren::<Runtime>::iter_prefix((collection_id, nft_id))
- .filter_map(|(child_id, is_child)|
- match is_child {
- true => Some(RmrkNftChild {
- collection_id: child_id.0.0,
- nft_id: child_id.1.0,
- }),
- false => None,
- }
- ).collect()
+ .filter_map(|((child_collection, child_token), _)| {
+ let rmrk_child_collection = RmrkCore::rmrk_collection_id(
+ child_collection
+ ).ok()?;
+
+ Some(RmrkNftChild {
+ collection_id: rmrk_child_collection,
+ nft_id: child_token.0,
+ })
+ }).collect()
)
}
fn collection_properties(collection_id: RmrkCollectionId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
use pallet_proxy_rmrk_core::misc::CollectionType;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() {
return Ok(Vec::new());
}
@@ -259,7 +277,10 @@
fn nft_properties(collection_id: RmrkCollectionId, nft_id: RmrkNftId, filter_keys: Option<Vec<RmrkPropertyKey>>) -> Result<Vec<RmrkPropertyInfo>, DispatchError> {
use pallet_proxy_rmrk_core::misc::NftType;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
let token_id = TokenId(nft_id);
if RmrkCore::ensure_nft_type(collection_id, token_id, NftType::Regular).is_err() {
@@ -283,7 +304,10 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType, ResourceType}};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
let nft_id = TokenId(nft_id);
@@ -332,7 +356,10 @@
fn nft_resource_priorities(collection_id: RmrkCollectionId, nft_id: RmrkNftId) -> Result<Vec<RmrkResourceId>, DispatchError> {
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
- let collection_id = RmrkCore::unique_collection_id(collection_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(collection_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
+ };
if RmrkCore::ensure_collection_type(collection_id, CollectionType::Regular).is_err() { return Ok(Vec::new()); }
let nft_id = TokenId(nft_id);
@@ -360,7 +387,10 @@
RmrkProperty, misc::{CollectionType},
};
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
+ };
let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
Ok(c) => c,
Err(_) => return Ok(None),
@@ -377,12 +407,11 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::{CollectionType, NftType}};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
-
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() { return Ok(Vec::new()); }
let parts = collection.collection_tokens()
.into_iter()
@@ -413,11 +442,13 @@
use pallet_proxy_rmrk_core::{RmrkProperty, misc::CollectionType};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(Vec::new()),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(Vec::new())
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+ return Ok(Vec::new());
+ }
let theme_names = collection.collection_tokens()
@@ -444,11 +475,13 @@
};
use pallet_common::CommonCollectionOperations;
- let collection_id = RmrkCore::unique_collection_id(base_id)?;
- let collection = match RmrkCore::get_typed_nft_collection(collection_id, CollectionType::Base) {
- Ok(c) => c,
- Err(_) => return Ok(None),
+ let collection_id = match RmrkCore::unique_collection_id(base_id) {
+ Ok(id) => id,
+ Err(_) => return Ok(None)
};
+ if RmrkCore::ensure_collection_type(collection_id, CollectionType::Base).is_err() {
+ return Ok(None);
+ }
let theme_info = collection.collection_tokens()
.into_iter()