--- a/pallets/common/src/lib.rs +++ b/pallets/common/src/lib.rs @@ -723,17 +723,20 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn create_multiple_items( &self, sender: T::CrossAccountId, to: T::CrossAccountId, data: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn create_multiple_items_ex( &self, sender: T::CrossAccountId, data: CreateItemExData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn burn_item( &self, @@ -748,6 +751,7 @@ to: T::CrossAccountId, token: TokenId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo; fn approve( &self, @@ -781,11 +785,12 @@ data: BoundedVec, ) -> DispatchResultWithPostInfo; - fn nest_token( + fn check_nesting( &self, sender: T::CrossAccountId, - from: (CollectionId, TokenId), + from: CollectionId, under: TokenId, + budget: &dyn Budget, ) -> DispatchResult; fn account_tokens(&self, account: T::CrossAccountId) -> Vec; --- a/pallets/evm-coder-substrate/src/lib.rs +++ b/pallets/evm-coder-substrate/src/lib.rs @@ -55,7 +55,6 @@ use frame_system::ensure_signed; pub use frame_support::dispatch::DispatchResult; - use frame_support::{pallet_prelude::*, traits::PalletInfo}; use frame_system::pallet_prelude::*; /// DispatchError is opaque, but we need to somehow extract correct error in case of OutOfGas failure --- a/pallets/fungible/src/common.rs +++ b/pallets/fungible/src/common.rs @@ -17,7 +17,7 @@ use core::marker::PhantomData; use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, BoundedVec}; -use up_data_structs::{TokenId, CreateItemExData, budget::Budget}; +use up_data_structs::{TokenId, CollectionId, CreateItemExData, budget::Budget}; use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight}; use sp_runtime::ArithmeticError; use sp_std::{vec::Vec, vec}; @@ -78,10 +78,11 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: up_data_structs::CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { match data { up_data_structs::CreateItemData::Fungible(data) => with_weight( - >::create_item(self, &sender, (to, data.value)), + >::create_item(self, &sender, (to, data.value), nesting_budget), >::create_item(), ), _ => fail!(>::NotFungibleDataUsedToMintFungibleCollectionToken), @@ -93,6 +94,7 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let mut sum: u128 = 0; for data in data { @@ -107,7 +109,7 @@ } with_weight( - >::create_item(self, &sender, (to, sum)), + >::create_item(self, &sender, (to, sum), nesting_budget), >::create_item(), ) } @@ -116,6 +118,7 @@ &self, sender: ::CrossAccountId, data: up_data_structs::CreateItemExData<::CrossAccountId>, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let weight = >::create_multiple_items_ex(&data); let data = match data { @@ -124,7 +127,7 @@ }; with_weight( - >::create_multiple_items(self, &sender, data.into_inner()), + >::create_multiple_items(self, &sender, data.into_inner(), nesting_budget), weight, ) } @@ -152,6 +155,7 @@ to: T::CrossAccountId, token: TokenId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { ensure!( token == TokenId::default(), @@ -159,7 +163,7 @@ ); with_weight( - >::transfer(self, &from, &to, amount), + >::transfer(self, &from, &to, amount, nesting_budget), >::transfer(), ) } @@ -230,11 +234,12 @@ fail!(>::FungibleItemsDontHaveData) } - fn nest_token( + fn check_nesting( &self, _sender: ::CrossAccountId, - _from: (up_data_structs::CollectionId, TokenId), + _from: CollectionId, _under: TokenId, + _budget: &dyn Budget, ) -> sp_runtime::DispatchResult { fail!(>::FungibleDisallowsNesting) } --- a/pallets/fungible/src/erc.rs +++ b/pallets/fungible/src/erc.rs @@ -81,8 +81,11 @@ let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let amount = amount.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); - >::transfer(self, &caller, &to, amount).map_err(|_| "transfer error")?; + >::transfer(self, &caller, &to, amount, &budget).map_err(|_| "transfer error")?; Ok(true) } #[weight(>::transfer_from())] --- a/pallets/fungible/src/lib.rs +++ b/pallets/fungible/src/lib.rs @@ -193,6 +193,7 @@ from: &T::CrossAccountId, to: &T::CrossAccountId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResult { ensure!( collection.limits.transfers_enabled(), @@ -222,12 +223,12 @@ let handle = >::try_get(target.0)?; let dispatch = T::CollectionDispatch::dispatch(handle); let dispatch = dispatch.as_dyn(); - - // ========= - dispatch.nest_token(from.clone(), (collection.id, TokenId::default()), target.1)?; + dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?; } + // ========= + if let Some(balance_to) = balance_to { // from != to if balance_from == 0 { @@ -257,6 +258,7 @@ collection: &FungibleHandle, sender: &T::CrossAccountId, data: BTreeMap, + nesting_budget: &dyn Budget, ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( @@ -285,6 +287,16 @@ .ok_or(ArithmeticError::Overflow)?; } + for (to, _) in balances.iter() { + if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) { + let handle = >::try_get(target.0)?; + let dispatch = T::CollectionDispatch::dispatch(handle); + let dispatch = dispatch.as_dyn(); + + dispatch.check_nesting(sender.clone(), collection.id, target.1, nesting_budget)?; + } + } + // ========= >::insert(collection.id, total_supply); @@ -407,7 +419,7 @@ // ========= - Self::transfer(collection, from, to, amount)?; + Self::transfer(collection, from, to, amount, nesting_budget)?; if let Some(allowance) = allowance { Self::set_allowance_unchecked(collection, from, spender, allowance); } @@ -437,7 +449,13 @@ collection: &FungibleHandle, sender: &T::CrossAccountId, data: CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResult { - Self::create_multiple_items(collection, sender, [(data.0, data.1)].into_iter().collect()) + Self::create_multiple_items( + collection, + sender, + [(data.0, data.1)].into_iter().collect(), + nesting_budget, + ) } } --- a/pallets/nonfungible/src/common.rs +++ b/pallets/nonfungible/src/common.rs @@ -89,9 +89,15 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: up_data_structs::CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { with_weight( - >::create_item(self, &sender, map_create_data::(data, &to)?), + >::create_item( + self, + &sender, + map_create_data::(data, &to)?, + nesting_budget, + ), >::create_item(), ) } @@ -101,6 +107,7 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let data = data .into_iter() @@ -109,7 +116,7 @@ let amount = data.len(); with_weight( - >::create_multiple_items(self, &sender, data), + >::create_multiple_items(self, &sender, data, nesting_budget), >::create_multiple_items(amount as u32), ) } @@ -118,6 +125,7 @@ &self, sender: ::CrossAccountId, data: up_data_structs::CreateItemExData<::CrossAccountId>, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let weight = >::create_multiple_items_ex(&data); let data = match data { @@ -126,7 +134,7 @@ }; with_weight( - >::create_multiple_items(self, &sender, data.into_inner()), + >::create_multiple_items(self, &sender, data.into_inner(), nesting_budget), weight, ) } @@ -154,11 +162,12 @@ to: T::CrossAccountId, token: TokenId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { ensure!(amount <= 1, >::NonfungibleItemsHaveNoAmount); if amount == 1 { with_weight( - >::transfer(self, &from, &to, token), + >::transfer(self, &from, &to, token, nesting_budget), >::transfer(), ) } else { @@ -239,13 +248,14 @@ ) } - fn nest_token( + fn check_nesting( &self, sender: T::CrossAccountId, - (from, _): (CollectionId, TokenId), + from: CollectionId, under: TokenId, + budget: &dyn Budget, ) -> sp_runtime::DispatchResult { - >::nest_token(self, sender, from, under) + >::check_nesting(self, sender, from, under, budget) } fn account_tokens(&self, account: T::CrossAccountId) -> Vec { --- a/pallets/nonfungible/src/erc.rs +++ b/pallets/nonfungible/src/erc.rs @@ -256,6 +256,10 @@ let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let token_id: u32 = token_id.try_into()?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + if >::get(self.id) .checked_add(1) .ok_or("item id overflow")? @@ -272,6 +276,7 @@ variable_data: BoundedVec::default(), owner: to, }, + &budget, ) .map_err(dispatch_to_evm::)?; @@ -296,6 +301,10 @@ let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); + if >::get(self.id) .checked_add(1) .ok_or("item id overflow")? @@ -314,6 +323,7 @@ variable_data: BoundedVec::default(), owner: to, }, + &budget, ) .map_err(dispatch_to_evm::)?; Ok(true) @@ -338,8 +348,11 @@ let caller = T::CrossAccountId::from_eth(caller); let to = T::CrossAccountId::from_eth(to); let token = token_id.try_into()?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); - >::transfer(self, &caller, &to, token).map_err(dispatch_to_evm::)?; + >::transfer(self, &caller, &to, token, &budget).map_err(dispatch_to_evm::)?; Ok(()) } @@ -409,6 +422,9 @@ let mut expected_index = >::get(self.id) .checked_add(1) .ok_or("item id overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); let total_tokens = token_ids.len(); for id in token_ids.into_iter() { @@ -426,7 +442,8 @@ }) .collect(); - >::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::)?; + >::create_multiple_items(self, &caller, data, &budget) + .map_err(dispatch_to_evm::)?; Ok(true) } @@ -447,6 +464,9 @@ let mut expected_index = >::get(self.id) .checked_add(1) .ok_or("item id overflow")?; + let budget = self + .recorder + .weight_calls_budget(>::find_parent()); let mut data = Vec::with_capacity(tokens.len()); for (id, token_uri) in tokens { @@ -465,7 +485,8 @@ }); } - >::create_multiple_items(self, &caller, data).map_err(dispatch_to_evm::)?; + >::create_multiple_items(self, &caller, data, &budget) + .map_err(dispatch_to_evm::)?; Ok(true) } } --- a/pallets/nonfungible/src/lib.rs +++ b/pallets/nonfungible/src/lib.rs @@ -251,6 +251,7 @@ from: &T::CrossAccountId, to: &T::CrossAccountId, token: TokenId, + nesting_budget: &dyn Budget, ) -> DispatchResult { ensure!( collection.limits.transfers_enabled(), @@ -294,12 +295,12 @@ let handle = >::try_get(target.0)?; let dispatch = T::CollectionDispatch::dispatch(handle); let dispatch = dispatch.as_dyn(); - - // ========= - dispatch.nest_token(from.clone(), (collection.id, token), target.1)?; + dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?; } + // ========= + >::insert( (collection.id, token), ItemData { @@ -340,6 +341,7 @@ collection: &NonfungibleHandle, sender: &T::CrossAccountId, data: Vec>, + nesting_budget: &dyn Budget, ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( @@ -379,6 +381,16 @@ ); } + for (to, _) in balances.iter() { + if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) { + let handle = >::try_get(target.0)?; + let dispatch = T::CollectionDispatch::dispatch(handle); + let dispatch = dispatch.as_dyn(); + + dispatch.check_nesting(sender.clone(), collection.id, target.1, nesting_budget)?; + } + } + // ========= >::insert(collection.id, tokens_minted); @@ -556,7 +568,7 @@ // ========= // Allowance is reset in [`transfer`] - Self::transfer(collection, from, to, token) + Self::transfer(collection, from, to, token, nesting_budget) } pub fn burn_from( @@ -595,35 +607,34 @@ Ok(()) } - pub fn nest_token( + pub fn check_nesting( handle: &NonfungibleHandle, sender: T::CrossAccountId, from: CollectionId, under: TokenId, + nesting_budget: &dyn Budget, ) -> DispatchResult { fn ensure_sender_allowed( collection: CollectionId, token: TokenId, sender: T::CrossAccountId, + budget: &dyn Budget, ) -> DispatchResult { ensure!( - >::get((collection, token)) - .ok_or(>::TokenNotFound)? - .owner - .conv_eq(&sender), + >::indirectly_owned(sender, collection, token, budget)?, >::OnlyOwnerAllowedToNest, ); Ok(()) } match handle.limits.nesting_rule() { NestingRule::Disabled => fail!(>::NestingIsDisabled), - NestingRule::Owner => ensure_sender_allowed::(from, under, sender)?, + NestingRule::Owner => ensure_sender_allowed::(from, under, sender, nesting_budget)?, NestingRule::OwnerRestricted(whitelist) => { ensure!( whitelist.contains(&from), >::SourceCollectionIsNotAllowedToNest ); - ensure_sender_allowed::(from, under, sender)? + ensure_sender_allowed::(from, under, sender, nesting_budget)? } } Ok(()) @@ -634,7 +645,8 @@ collection: &NonfungibleHandle, sender: &T::CrossAccountId, data: CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResult { - Self::create_multiple_items(collection, sender, vec![data]) + Self::create_multiple_items(collection, sender, vec![data], nesting_budget) } } --- a/pallets/refungible/src/common.rs +++ b/pallets/refungible/src/common.rs @@ -19,7 +19,8 @@ use sp_std::collections::btree_map::BTreeMap; use frame_support::{dispatch::DispatchResultWithPostInfo, fail, weights::Weight, BoundedVec}; use up_data_structs::{ - TokenId, CustomDataLimit, CreateItemExData, CreateRefungibleExData, budget::Budget, + CollectionId, TokenId, CustomDataLimit, CreateItemExData, CreateRefungibleExData, + budget::Budget, }; use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight}; use sp_runtime::DispatchError; @@ -120,9 +121,15 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: up_data_structs::CreateItemData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { with_weight( - >::create_item(self, &sender, map_create_data::(data, &to)?), + >::create_item( + self, + &sender, + map_create_data::(data, &to)?, + nesting_budget, + ), >::create_item(), ) } @@ -132,6 +139,7 @@ sender: T::CrossAccountId, to: T::CrossAccountId, data: Vec, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let data = data .into_iter() @@ -140,7 +148,7 @@ let amount = data.len(); with_weight( - >::create_multiple_items(self, &sender, data), + >::create_multiple_items(self, &sender, data, nesting_budget), >::create_multiple_items(amount as u32), ) } @@ -149,6 +157,7 @@ &self, sender: ::CrossAccountId, data: CreateItemExData, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { let weight = >::create_multiple_items_ex(&data); let data = match data { @@ -162,7 +171,7 @@ }; with_weight( - >::create_multiple_items(self, &sender, data), + >::create_multiple_items(self, &sender, data, nesting_budget), weight, ) } @@ -185,9 +194,10 @@ to: T::CrossAccountId, token: TokenId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResultWithPostInfo { with_weight( - >::transfer(self, &from, &to, token, amount), + >::transfer(self, &from, &to, token, amount, nesting_budget), >::transfer(), ) } @@ -247,11 +257,12 @@ ) } - fn nest_token( + fn check_nesting( &self, _sender: ::CrossAccountId, - _from: (up_data_structs::CollectionId, TokenId), + _from: CollectionId, _under: TokenId, + _budget: &dyn Budget, ) -> sp_runtime::DispatchResult { fail!(>::RefungibleDisallowsNesting) } --- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -289,6 +289,7 @@ to: &T::CrossAccountId, token: TokenId, amount: u128, + nesting_budget: &dyn Budget, ) -> DispatchResult { ensure!( collection.limits.transfers_enabled(), @@ -351,10 +352,10 @@ let dispatch = T::CollectionDispatch::dispatch(handle); let dispatch = dispatch.as_dyn(); - // ========= + dispatch.check_nesting(from.clone(), collection.id, target.1, nesting_budget)?; + } - dispatch.nest_token(from.clone(), (collection.id, token), target.1)?; - } + // ========= if let Some(balance_to) = balance_to { // from != to @@ -389,6 +390,7 @@ collection: &RefungibleHandle, sender: &T::CrossAccountId, data: Vec>, + nesting_budget: &dyn Budget, ) -> DispatchResult { if !collection.is_owner_or_admin(sender) { ensure!( @@ -453,6 +455,23 @@ } } + for token in data.iter() { + for (to, _) in token.users.iter() { + if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) { + let handle = >::try_get(target.0)?; + let dispatch = T::CollectionDispatch::dispatch(handle); + let dispatch = dispatch.as_dyn(); + + dispatch.check_nesting( + sender.clone(), + collection.id, + target.1, + nesting_budget, + )?; + } + } + } + // ========= >::insert(collection.id, tokens_minted); @@ -591,7 +610,7 @@ // ========= - Self::transfer(collection, from, to, token, amount)?; + Self::transfer(collection, from, to, token, amount, nesting_budget)?; if let Some(allowance) = allowance { Self::set_allowance_unchecked(collection, from, spender, token, allowance); } @@ -648,7 +667,8 @@ collection: &RefungibleHandle, sender: &T::CrossAccountId, data: CreateRefungibleExData, + nesting_budget: &dyn Budget, ) -> DispatchResult { - Self::create_multiple_items(collection, sender, vec![data]) + Self::create_multiple_items(collection, sender, vec![data], nesting_budget) } } --- a/pallets/structure/src/lib.rs +++ b/pallets/structure/src/lib.rs @@ -19,7 +19,6 @@ use frame_support::Parameter; use frame_support::dispatch::{GetDispatchInfo, UnfilteredDispatchable}; use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; use super::*; --- a/pallets/unique/src/lib.rs +++ b/pallets/unique/src/lib.rs @@ -693,8 +693,9 @@ #[transactional] pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(2); - dispatch_call::(collection_id, |d| d.create_item(sender, owner, data)) + dispatch_call::(collection_id, |d| d.create_item(sender, owner, data, &budget)) } /// This method creates multiple items in a collection created with CreateCollection method. @@ -720,16 +721,18 @@ pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec) -> DispatchResultWithPostInfo { ensure!(!items_data.is_empty(), Error::::EmptyArgument); let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(2); - dispatch_call::(collection_id, |d| d.create_multiple_items(sender, owner, items_data)) + dispatch_call::(collection_id, |d| d.create_multiple_items(sender, owner, items_data, &budget)) } #[weight = >::create_multiple_items_ex(&data)] #[transactional] pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData) -> DispatchResultWithPostInfo { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(2); - dispatch_call::(collection_id, |d| d.create_multiple_items_ex(sender, data)) + dispatch_call::(collection_id, |d| d.create_multiple_items_ex(sender, data, &budget)) } // TODO! transaction weight @@ -839,8 +842,9 @@ #[transactional] pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo { let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?); + let budget = budget::Value::new(2); - dispatch_call::(collection_id, |d| d.transfer(sender, recipient, item_id, value)) + dispatch_call::(collection_id, |d| d.transfer(sender, recipient, item_id, value, &budget)) } /// Set, change, or remove approved address to transfer the ownership of the NFT.