difftreelog
feat check nesting rule
in: master
10 files changed
pallets/common/src/eth.rsdiffbeforeafterboth--- a/pallets/common/src/eth.rs
+++ b/pallets/common/src/eth.rs
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
-use up_data_structs::{CollectionId, TokenId};
+use up_data_structs::CollectionId;
use sp_core::H160;
// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -345,6 +345,13 @@
/// Not sufficient founds to perform action
NotSufficientFounds,
+
+ /// Collection has nesting disabled
+ NestingIsDisabled,
+ /// Only owner may nest tokens under this collection
+ OnlyOwnerAllowedToNest,
+ /// Only tokens from specific collections may nest tokens under this
+ SourceCollectionIsNotAllowedToNest,
}
#[pallet::storage]
@@ -400,8 +407,6 @@
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_runtime_upgrade() -> Weight {
- let mut weight = 0;
-
if StorageVersion::get::<Pallet<T>>() < StorageVersion::new(1) {
use up_data_structs::{CollectionVersion1, CollectionVersion2};
<CollectionById<T>>::translate_values::<CollectionVersion1<T::AccountId>, _>(|v| {
@@ -409,7 +414,7 @@
});
}
- weight
+ 0
}
}
}
@@ -774,6 +779,13 @@
data: BoundedVec<u8, CustomDataLimit>,
) -> DispatchResultWithPostInfo;
+ fn nest_token(
+ &self,
+ sender: T::CrossAccountId,
+ from: (CollectionId, TokenId),
+ under: TokenId,
+ ) -> DispatchResult;
+
fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;
fn token_exists(&self, token: TokenId) -> bool;
fn last_token_id(&self) -> TokenId;
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -228,6 +228,15 @@
fail!(<Error<T>>::FungibleItemsDontHaveData)
}
+ fn nest_token(
+ &self,
+ _sender: <T>::CrossAccountId,
+ _from: (up_data_structs::CollectionId, TokenId),
+ _under: TokenId,
+ ) -> sp_runtime::DispatchResult {
+ fail!(<Error<T>>::FungibleDisallowsNesting)
+ }
+
fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {
if <Balance<T>>::get((self.id, account)) != 0 {
vec![TokenId::default()]
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -18,9 +18,14 @@
use core::ops::Deref;
use frame_support::{ensure};
-use up_data_structs::{AccessMode, CollectionId, TokenId, CreateCollectionData};
-use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
use pallet_evm::account::CrossAccountId;
+use up_data_structs::{
+ AccessMode, CollectionId, TokenId, CreateCollectionData, mapping::TokenAddressMapping,
+};
+use pallet_common::{
+ Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
+ CollectionHandle, dispatch::CollectionDispatch,
+};
use pallet_evm_coder_substrate::WithRecorder;
use sp_core::H160;
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
@@ -52,6 +57,8 @@
FungibleItemsHaveNoId,
/// Tried to set data for fungible item
FungibleItemsDontHaveData,
+ /// Fungible token does not support nested
+ FungibleDisallowsNesting,
}
#[pallet::config]
@@ -207,7 +214,15 @@
None
};
- // =========
+ if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
+ let handle = <CollectionHandle<T>>::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)?;
+ }
if let Some(balance_to) = balance_to {
// from != to
pallets/nonfungible/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, BoundedVec};20use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData};21use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};22use sp_runtime::DispatchError;23use sp_std::vec::Vec;2425use crate::{26 AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,27 SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,28};2930pub struct CommonWeights<T: Config>(PhantomData<T>);31impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {32 fn create_item() -> Weight {33 <SelfWeightOf<T>>::create_item()34 }3536 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {37 match data {38 CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32),39 _ => 0,40 }41 }4243 fn create_multiple_items(amount: u32) -> Weight {44 <SelfWeightOf<T>>::create_multiple_items(amount)45 }4647 fn burn_item() -> Weight {48 <SelfWeightOf<T>>::burn_item()49 }5051 fn transfer() -> Weight {52 <SelfWeightOf<T>>::transfer()53 }5455 fn approve() -> Weight {56 <SelfWeightOf<T>>::approve()57 }5859 fn transfer_from() -> Weight {60 <SelfWeightOf<T>>::transfer_from()61 }6263 fn burn_from() -> Weight {64 <SelfWeightOf<T>>::burn_from()65 }6667 fn set_variable_metadata(bytes: u32) -> Weight {68 <SelfWeightOf<T>>::set_variable_metadata(bytes)69 }70}7172fn map_create_data<T: Config>(73 data: up_data_structs::CreateItemData,74 to: &T::CrossAccountId,75) -> Result<CreateItemData<T>, DispatchError> {76 match data {77 up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData::<T> {78 const_data: data.const_data,79 variable_data: data.variable_data,80 owner: to.clone(),81 }),82 _ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),83 }84}8586impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {87 fn create_item(88 &self,89 sender: T::CrossAccountId,90 to: T::CrossAccountId,91 data: up_data_structs::CreateItemData,92 ) -> DispatchResultWithPostInfo {93 with_weight(94 <Pallet<T>>::create_item(self, &sender, map_create_data::<T>(data, &to)?),95 <CommonWeights<T>>::create_item(),96 )97 }9899 fn create_multiple_items(100 &self,101 sender: T::CrossAccountId,102 to: T::CrossAccountId,103 data: Vec<up_data_structs::CreateItemData>,104 ) -> DispatchResultWithPostInfo {105 let data = data106 .into_iter()107 .map(|d| map_create_data::<T>(d, &to))108 .collect::<Result<Vec<_>, DispatchError>>()?;109110 let amount = data.len();111 with_weight(112 <Pallet<T>>::create_multiple_items(self, &sender, data),113 <CommonWeights<T>>::create_multiple_items(amount as u32),114 )115 }116117 fn create_multiple_items_ex(118 &self,119 sender: <T>::CrossAccountId,120 data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,121 ) -> DispatchResultWithPostInfo {122 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);123 let data = match data {124 up_data_structs::CreateItemExData::NFT(nft) => nft,125 _ => fail!(Error::<T>::NotNonfungibleDataUsedToMintFungibleCollectionToken),126 };127128 with_weight(129 <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),130 weight,131 )132 }133134 fn burn_item(135 &self,136 sender: T::CrossAccountId,137 token: TokenId,138 amount: u128,139 ) -> DispatchResultWithPostInfo {140 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);141 if amount == 1 {142 with_weight(143 <Pallet<T>>::burn(self, &sender, token),144 <CommonWeights<T>>::burn_item(),145 )146 } else {147 Ok(().into())148 }149 }150151 fn transfer(152 &self,153 from: T::CrossAccountId,154 to: T::CrossAccountId,155 token: TokenId,156 amount: u128,157 ) -> DispatchResultWithPostInfo {158 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);159 if amount == 1 {160 with_weight(161 <Pallet<T>>::transfer(self, &from, &to, token),162 <CommonWeights<T>>::transfer(),163 )164 } else {165 Ok(().into())166 }167 }168169 fn approve(170 &self,171 sender: T::CrossAccountId,172 spender: T::CrossAccountId,173 token: TokenId,174 amount: u128,175 ) -> DispatchResultWithPostInfo {176 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);177178 with_weight(179 if amount == 1 {180 <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))181 } else {182 <Pallet<T>>::set_allowance(self, &sender, token, None)183 },184 <CommonWeights<T>>::approve(),185 )186 }187188 fn transfer_from(189 &self,190 sender: T::CrossAccountId,191 from: T::CrossAccountId,192 to: T::CrossAccountId,193 token: TokenId,194 amount: u128,195 ) -> DispatchResultWithPostInfo {196 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);197198 if amount == 1 {199 with_weight(200 <Pallet<T>>::transfer_from(self, &sender, &from, &to, token),201 <CommonWeights<T>>::transfer_from(),202 )203 } else {204 Ok(().into())205 }206 }207208 fn burn_from(209 &self,210 sender: T::CrossAccountId,211 from: T::CrossAccountId,212 token: TokenId,213 amount: u128,214 ) -> DispatchResultWithPostInfo {215 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);216217 if amount == 1 {218 with_weight(219 <Pallet<T>>::burn_from(self, &sender, &from, token),220 <CommonWeights<T>>::burn_from(),221 )222 } else {223 Ok(().into())224 }225 }226227 fn set_variable_metadata(228 &self,229 sender: T::CrossAccountId,230 token: TokenId,231 data: BoundedVec<u8, CustomDataLimit>,232 ) -> DispatchResultWithPostInfo {233 let len = data.len();234 with_weight(235 <Pallet<T>>::set_variable_metadata(self, &sender, token, data),236 <CommonWeights<T>>::set_variable_metadata(len as u32),237 )238 }239240 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {241 <Owned<T>>::iter_prefix((self.id, account))242 .map(|(id, _)| id)243 .collect()244 }245246 fn token_exists(&self, token: TokenId) -> bool {247 <Pallet<T>>::token_exists(self, token)248 }249250 fn last_token_id(&self) -> TokenId {251 TokenId(<TokensMinted<T>>::get(self.id))252 }253254 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId> {255 <TokenData<T>>::get((self.id, token)).map(|t| t.owner)256 }257 fn const_metadata(&self, token: TokenId) -> Vec<u8> {258 <TokenData<T>>::get((self.id, token))259 .map(|t| t.const_data)260 .unwrap_or_default()261 .into_inner()262 }263 fn variable_metadata(&self, token: TokenId) -> Vec<u8> {264 <TokenData<T>>::get((self.id, token))265 .map(|t| t.variable_data)266 .unwrap_or_default()267 .into_inner()268 }269270 fn collection_tokens(&self) -> u32 {271 <Pallet<T>>::total_supply(self)272 }273274 fn account_balance(&self, account: T::CrossAccountId) -> u32 {275 <AccountBalance<T>>::get((self.id, account))276 }277278 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {279 if <TokenData<T>>::get((self.id, token))280 .map(|a| a.owner == account)281 .unwrap_or(false)282 {283 1284 } else {285 0286 }287 }288289 fn allowance(290 &self,291 sender: T::CrossAccountId,292 spender: T::CrossAccountId,293 token: TokenId,294 ) -> u128 {295 if <TokenData<T>>::get((self.id, token))296 .map(|a| a.owner != sender)297 .unwrap_or(true)298 {299 0300 } else if <Allowance<T>>::get((self.id, token)) == Some(spender) {301 1302 } else {303 0304 }305 }306}1// 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, BoundedVec};20use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData, CollectionId};21use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};22use sp_runtime::DispatchError;23use sp_std::vec::Vec;2425use crate::{26 AccountBalance, Allowance, Config, CreateItemData, Error, NonfungibleHandle, Owned, Pallet,27 SelfWeightOf, TokenData, weights::WeightInfo, TokensMinted,28};2930pub struct CommonWeights<T: Config>(PhantomData<T>);31impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {32 fn create_item() -> Weight {33 <SelfWeightOf<T>>::create_item()34 }3536 fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {37 match data {38 CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32),39 _ => 0,40 }41 }4243 fn create_multiple_items(amount: u32) -> Weight {44 <SelfWeightOf<T>>::create_multiple_items(amount)45 }4647 fn burn_item() -> Weight {48 <SelfWeightOf<T>>::burn_item()49 }5051 fn transfer() -> Weight {52 <SelfWeightOf<T>>::transfer()53 }5455 fn approve() -> Weight {56 <SelfWeightOf<T>>::approve()57 }5859 fn transfer_from() -> Weight {60 <SelfWeightOf<T>>::transfer_from()61 }6263 fn burn_from() -> Weight {64 <SelfWeightOf<T>>::burn_from()65 }6667 fn set_variable_metadata(bytes: u32) -> Weight {68 <SelfWeightOf<T>>::set_variable_metadata(bytes)69 }70}7172fn map_create_data<T: Config>(73 data: up_data_structs::CreateItemData,74 to: &T::CrossAccountId,75) -> Result<CreateItemData<T>, DispatchError> {76 match data {77 up_data_structs::CreateItemData::NFT(data) => Ok(CreateItemData::<T> {78 const_data: data.const_data,79 variable_data: data.variable_data,80 owner: to.clone(),81 }),82 _ => fail!(<Error<T>>::NotNonfungibleDataUsedToMintFungibleCollectionToken),83 }84}8586impl<T: Config> CommonCollectionOperations<T> for NonfungibleHandle<T> {87 fn create_item(88 &self,89 sender: T::CrossAccountId,90 to: T::CrossAccountId,91 data: up_data_structs::CreateItemData,92 ) -> DispatchResultWithPostInfo {93 with_weight(94 <Pallet<T>>::create_item(self, &sender, map_create_data::<T>(data, &to)?),95 <CommonWeights<T>>::create_item(),96 )97 }9899 fn create_multiple_items(100 &self,101 sender: T::CrossAccountId,102 to: T::CrossAccountId,103 data: Vec<up_data_structs::CreateItemData>,104 ) -> DispatchResultWithPostInfo {105 let data = data106 .into_iter()107 .map(|d| map_create_data::<T>(d, &to))108 .collect::<Result<Vec<_>, DispatchError>>()?;109110 let amount = data.len();111 with_weight(112 <Pallet<T>>::create_multiple_items(self, &sender, data),113 <CommonWeights<T>>::create_multiple_items(amount as u32),114 )115 }116117 fn create_multiple_items_ex(118 &self,119 sender: <T>::CrossAccountId,120 data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,121 ) -> DispatchResultWithPostInfo {122 let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);123 let data = match data {124 up_data_structs::CreateItemExData::NFT(nft) => nft,125 _ => fail!(Error::<T>::NotNonfungibleDataUsedToMintFungibleCollectionToken),126 };127128 with_weight(129 <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),130 weight,131 )132 }133134 fn burn_item(135 &self,136 sender: T::CrossAccountId,137 token: TokenId,138 amount: u128,139 ) -> DispatchResultWithPostInfo {140 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);141 if amount == 1 {142 with_weight(143 <Pallet<T>>::burn(self, &sender, token),144 <CommonWeights<T>>::burn_item(),145 )146 } else {147 Ok(().into())148 }149 }150151 fn transfer(152 &self,153 from: T::CrossAccountId,154 to: T::CrossAccountId,155 token: TokenId,156 amount: u128,157 ) -> DispatchResultWithPostInfo {158 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);159 if amount == 1 {160 with_weight(161 <Pallet<T>>::transfer(self, &from, &to, token),162 <CommonWeights<T>>::transfer(),163 )164 } else {165 Ok(().into())166 }167 }168169 fn approve(170 &self,171 sender: T::CrossAccountId,172 spender: T::CrossAccountId,173 token: TokenId,174 amount: u128,175 ) -> DispatchResultWithPostInfo {176 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);177178 with_weight(179 if amount == 1 {180 <Pallet<T>>::set_allowance(self, &sender, token, Some(&spender))181 } else {182 <Pallet<T>>::set_allowance(self, &sender, token, None)183 },184 <CommonWeights<T>>::approve(),185 )186 }187188 fn transfer_from(189 &self,190 sender: T::CrossAccountId,191 from: T::CrossAccountId,192 to: T::CrossAccountId,193 token: TokenId,194 amount: u128,195 ) -> DispatchResultWithPostInfo {196 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);197198 if amount == 1 {199 with_weight(200 <Pallet<T>>::transfer_from(self, &sender, &from, &to, token),201 <CommonWeights<T>>::transfer_from(),202 )203 } else {204 Ok(().into())205 }206 }207208 fn burn_from(209 &self,210 sender: T::CrossAccountId,211 from: T::CrossAccountId,212 token: TokenId,213 amount: u128,214 ) -> DispatchResultWithPostInfo {215 ensure!(amount <= 1, <Error<T>>::NonfungibleItemsHaveNoAmount);216217 if amount == 1 {218 with_weight(219 <Pallet<T>>::burn_from(self, &sender, &from, token),220 <CommonWeights<T>>::burn_from(),221 )222 } else {223 Ok(().into())224 }225 }226227 fn set_variable_metadata(228 &self,229 sender: T::CrossAccountId,230 token: TokenId,231 data: BoundedVec<u8, CustomDataLimit>,232 ) -> DispatchResultWithPostInfo {233 let len = data.len();234 with_weight(235 <Pallet<T>>::set_variable_metadata(self, &sender, token, data),236 <CommonWeights<T>>::set_variable_metadata(len as u32),237 )238 }239240 fn nest_token(241 &self,242 sender: T::CrossAccountId,243 (from, _): (CollectionId, TokenId),244 under: TokenId,245 ) -> sp_runtime::DispatchResult {246 <Pallet<T>>::nest_token(self, sender, from, under)247 }248249 fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {250 <Owned<T>>::iter_prefix((self.id, account))251 .map(|(id, _)| id)252 .collect()253 }254255 fn token_exists(&self, token: TokenId) -> bool {256 <Pallet<T>>::token_exists(self, token)257 }258259 fn last_token_id(&self) -> TokenId {260 TokenId(<TokensMinted<T>>::get(self.id))261 }262263 fn token_owner(&self, token: TokenId) -> Option<T::CrossAccountId> {264 <TokenData<T>>::get((self.id, token)).map(|t| t.owner)265 }266 fn const_metadata(&self, token: TokenId) -> Vec<u8> {267 <TokenData<T>>::get((self.id, token))268 .map(|t| t.const_data)269 .unwrap_or_default()270 .into_inner()271 }272 fn variable_metadata(&self, token: TokenId) -> Vec<u8> {273 <TokenData<T>>::get((self.id, token))274 .map(|t| t.variable_data)275 .unwrap_or_default()276 .into_inner()277 }278279 fn collection_tokens(&self) -> u32 {280 <Pallet<T>>::total_supply(self)281 }282283 fn account_balance(&self, account: T::CrossAccountId) -> u32 {284 <AccountBalance<T>>::get((self.id, account))285 }286287 fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128 {288 if <TokenData<T>>::get((self.id, token))289 .map(|a| a.owner == account)290 .unwrap_or(false)291 {292 1293 } else {294 0295 }296 }297298 fn allowance(299 &self,300 sender: T::CrossAccountId,301 spender: T::CrossAccountId,302 token: TokenId,303 ) -> u128 {304 if <TokenData<T>>::get((self.id, token))305 .map(|a| a.owner != sender)306 .unwrap_or(true)307 {308 0309 } else if <Allowance<T>>::get((self.id, token)) == Some(spender) {310 1311 } else {312 0313 }314 }315}pallets/nonfungible/src/lib.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -17,12 +17,16 @@
#![cfg_attr(not(feature = "std"), no_std)]
use erc::ERC721Events;
-use frame_support::{BoundedVec, ensure};
+use frame_support::{BoundedVec, ensure, fail};
use up_data_structs::{
AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
+ mapping::TokenAddressMapping, NestingRule,
};
-use pallet_common::{Error as CommonError, Pallet as PalletCommon, Event as CommonEvent};
use pallet_evm::account::CrossAccountId;
+use pallet_common::{
+ Error as CommonError, Pallet as PalletCommon, Event as CommonEvent,
+ CollectionHandle, dispatch::CollectionDispatch,
+};
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
use sp_core::H160;
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
@@ -282,8 +286,16 @@
None
};
- // =========
+ if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
+ let handle = <CollectionHandle<T>>::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)?;
+ }
+
<TokenData<T>>::insert(
(collection.id, token),
ItemData {
@@ -567,6 +579,40 @@
Ok(())
}
+ pub fn nest_token(
+ handle: &NonfungibleHandle<T>,
+ sender: T::CrossAccountId,
+ from: CollectionId,
+ under: TokenId,
+ ) -> DispatchResult {
+ fn ensure_sender_allowed<T: Config>(
+ collection: CollectionId,
+ token: TokenId,
+ sender: T::CrossAccountId,
+ ) -> DispatchResult {
+ ensure!(
+ <TokenData<T>>::get((collection, token))
+ .ok_or(<CommonError<T>>::TokenNotFound)?
+ .owner
+ .conv_eq(&sender),
+ <CommonError<T>>::OnlyOwnerAllowedToNest,
+ );
+ Ok(())
+ }
+ match handle.limits.nesting_rule() {
+ NestingRule::Disabled => fail!(<CommonError<T>>::NestingIsDisabled),
+ NestingRule::Owner => ensure_sender_allowed::<T>(from, under, sender)?,
+ NestingRule::OwnerRestricted(whitelist) => {
+ ensure!(
+ whitelist.contains(&from),
+ <CommonError<T>>::SourceCollectionIsNotAllowedToNest
+ );
+ ensure_sender_allowed::<T>(from, under, sender)?
+ }
+ }
+ Ok(())
+ }
+
/// Delegated to `create_multiple_items`
pub fn create_item(
collection: &NonfungibleHandle<T>,
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -243,6 +243,15 @@
)
}
+ fn nest_token(
+ &self,
+ _sender: <T>::CrossAccountId,
+ _from: (up_data_structs::CollectionId, TokenId),
+ _under: TokenId,
+ ) -> sp_runtime::DispatchResult {
+ fail!(<Error<T>>::RefungibleDisallowsNesting)
+ }
+
fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {
<Owned<T>>::iter_prefix((self.id, account))
.map(|(id, _)| id)
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -19,10 +19,13 @@
use frame_support::{ensure, BoundedVec};
use up_data_structs::{
AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
- CreateCollectionData, CreateRefungibleExData,
+ CreateCollectionData, CreateRefungibleExData, mapping::TokenAddressMapping,
};
-use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
use pallet_evm::account::CrossAccountId;
+use pallet_common::{
+ Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
+ CollectionHandle, dispatch::CollectionDispatch,
+};
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
use core::ops::Deref;
@@ -56,6 +59,8 @@
NotRefungibleDataUsedToMintFungibleCollectionToken,
/// Maximum refungibility exceeded
WrongRefungiblePieces,
+ /// Refungible token can't nest other tokens
+ RefungibleDisallowsNesting,
}
#[pallet::config]
@@ -338,7 +343,15 @@
None
};
- // =========
+ if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {
+ let handle = <CollectionHandle<T>>::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)?;
+ }
if let Some(balance_to) = balance_to {
// from != to
primitives/data-structs/src/bounded.rsdiffbeforeafterboth--- /dev/null
+++ b/primitives/data-structs/src/bounded.rs
@@ -0,0 +1,138 @@
+use core::fmt;
+use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
+use sp_std::vec::Vec;
+
+use frame_support::{
+ BoundedVec,
+ storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},
+};
+
+/// BoundedVec doesn't supports serde
+#[cfg(feature = "serde1")]
+pub mod vec_serde {
+ use core::convert::TryFrom;
+ use frame_support::{BoundedVec, traits::Get};
+ use serde::{
+ ser::{self, Serialize},
+ de::{self, Deserialize, Error},
+ };
+ use sp_std::vec::Vec;
+
+ pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>
+ where
+ D: ser::Serializer,
+ V: Serialize,
+ {
+ (value as &Vec<_>).serialize(serializer)
+ }
+
+ pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ V: de::Deserialize<'de>,
+ S: Get<u32>,
+ {
+ // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?
+ let vec = <Vec<V>>::deserialize(deserializer)?;
+ let len = vec.len();
+ TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
+ }
+}
+
+pub fn vec_debug<V, S>(v: &BoundedVec<V, S>, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
+where
+ V: fmt::Debug,
+{
+ use core::fmt::Debug;
+ (&v as &Vec<V>).fmt(f)
+}
+
+#[cfg(feature = "serde1")]
+#[allow(dead_code)]
+pub mod map_serde {
+ use core::convert::TryFrom;
+ use sp_std::collections::btree_map::BTreeMap;
+ use frame_support::{traits::Get, storage::bounded_btree_map::BoundedBTreeMap};
+ use serde::{
+ ser::{self, Serialize},
+ de::{self, Deserialize, Error},
+ };
+ pub fn serialize<D, K, V, S>(
+ value: &BoundedBTreeMap<K, V, S>,
+ serializer: D,
+ ) -> Result<D::Ok, D::Error>
+ where
+ D: ser::Serializer,
+ K: Serialize + Ord,
+ V: Serialize,
+ {
+ (value as &BTreeMap<_, _>).serialize(serializer)
+ }
+
+ pub fn deserialize<'de, D, K, V, S>(
+ deserializer: D,
+ ) -> Result<BoundedBTreeMap<K, V, S>, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ K: de::Deserialize<'de> + Ord,
+ V: de::Deserialize<'de>,
+ S: Get<u32>,
+ {
+ let map = <BTreeMap<K, V>>::deserialize(deserializer)?;
+ let len = map.len();
+ TryFrom::try_from(map).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
+ }
+}
+
+pub fn map_debug<K, V, S>(
+ v: &BoundedBTreeMap<K, V, S>,
+ f: &mut fmt::Formatter,
+) -> Result<(), fmt::Error>
+where
+ K: fmt::Debug + Ord,
+ V: fmt::Debug,
+{
+ use core::fmt::Debug;
+ (&v as &BTreeMap<K, V>).fmt(f)
+}
+
+#[cfg(feature = "serde1")]
+#[allow(dead_code)]
+pub mod set_serde {
+ use core::convert::TryFrom;
+ use sp_std::collections::btree_set::BTreeSet;
+ use frame_support::{traits::Get, storage::bounded_btree_set::BoundedBTreeSet};
+ use serde::{
+ ser::{self, Serialize},
+ de::{self, Deserialize, Error},
+ };
+ pub fn serialize<D, K, S>(
+ value: &BoundedBTreeSet<K, S>,
+ serializer: D,
+ ) -> Result<D::Ok, D::Error>
+ where
+ D: ser::Serializer,
+ K: Serialize + Ord,
+ {
+ (value as &BTreeSet<_>).serialize(serializer)
+ }
+
+ pub fn deserialize<'de, D, K, S>(deserializer: D) -> Result<BoundedBTreeSet<K, S>, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ K: de::Deserialize<'de> + Ord,
+ S: Get<u32>,
+ {
+ let map = <BTreeSet<K>>::deserialize(deserializer)?;
+ let len = map.len();
+ TryFrom::try_from(map).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
+ }
+}
+
+pub fn set_debug<K, S>(v: &BoundedBTreeSet<K, S>, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
+where
+ K: fmt::Debug + Ord,
+{
+ use core::fmt::Debug;
+ (&v as &BTreeSet<K>).fmt(f)
+}
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -22,9 +22,7 @@
};
use frame_support::{
storage::{bounded_btree_map::BoundedBTreeMap, bounded_btree_set::BoundedBTreeSet},
- traits::ConstU16,
};
-use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
@@ -36,6 +34,7 @@
use derivative::Derivative;
use scale_info::TypeInfo;
+mod bounded;
pub mod mapping;
mod migration;
@@ -255,14 +254,14 @@
pub owner: AccountId,
pub mode: CollectionMode,
pub access: AccessMode,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
pub mint_mode: bool,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
pub schema_version: SchemaVersion,
pub sponsorship: SponsorshipState<AccountId>,
@@ -272,9 +271,9 @@
#[version(2.., upper(limits.into()))]
pub limits: CollectionLimitsVersion2,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,
pub meta_update_permission: MetaUpdatePermission,
}
@@ -286,20 +285,20 @@
#[derivative(Default(value = "CollectionMode::NFT"))]
pub mode: CollectionMode,
pub access: Option<AccessMode>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub offchain_schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,
pub schema_version: Option<SchemaVersion>,
pub pending_sponsor: Option<AccountId>,
pub limits: Option<CollectionLimits>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub variable_on_chain_schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
pub const_on_chain_schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>,
pub meta_update_permission: Option<MetaUpdatePermission>,
}
@@ -410,8 +409,8 @@
Owner,
/// Owner can nest tokens from specified collections
OwnerRestricted(
- #[cfg_attr(feature = "serde1", serde(with = "bounded_set_serde"))]
- #[derivative(Debug(format_with = "bounded_set_debug"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::set_serde"))]
+ #[derivative(Debug(format_with = "bounded::set_debug"))]
BoundedBTreeSet<CollectionId, ConstU32<16>>,
),
}
@@ -421,150 +420,17 @@
pub enum SponsoringRateLimit {
SponsoringDisabled,
Blocks(u32),
-}
-
-/// BoundedVec doesn't supports serde
-#[cfg(feature = "serde1")]
-mod bounded_serde {
- use core::convert::TryFrom;
- use frame_support::{BoundedVec, traits::Get};
- use serde::{
- ser::{self, Serialize},
- de::{self, Deserialize, Error},
- };
- use sp_std::vec::Vec;
-
- pub fn serialize<D, V, S>(value: &BoundedVec<V, S>, serializer: D) -> Result<D::Ok, D::Error>
- where
- D: ser::Serializer,
- V: Serialize,
- {
- (value as &Vec<_>).serialize(serializer)
- }
-
- pub fn deserialize<'de, D, V, S>(deserializer: D) -> Result<BoundedVec<V, S>, D::Error>
- where
- D: de::Deserializer<'de>,
- V: de::Deserialize<'de>,
- S: Get<u32>,
- {
- // TODO: Implement custom visitor, which will limit vec size at parse time? Will serde only be used by chainspec?
- let vec = <Vec<V>>::deserialize(deserializer)?;
- let len = vec.len();
- TryFrom::try_from(vec).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
- }
-}
-
-fn bounded_debug<V, S>(v: &BoundedVec<V, S>, f: &mut fmt::Formatter) -> Result<(), fmt::Error>
-where
- V: fmt::Debug,
-{
- use core::fmt::Debug;
- (&v as &Vec<V>).fmt(f)
-}
-
-#[cfg(feature = "serde1")]
-#[allow(dead_code)]
-mod bounded_map_serde {
- use core::convert::TryFrom;
- use sp_std::collections::btree_map::BTreeMap;
- use frame_support::{traits::Get, storage::bounded_btree_map::BoundedBTreeMap};
- use serde::{
- ser::{self, Serialize},
- de::{self, Deserialize, Error},
- };
- pub fn serialize<D, K, V, S>(
- value: &BoundedBTreeMap<K, V, S>,
- serializer: D,
- ) -> Result<D::Ok, D::Error>
- where
- D: ser::Serializer,
- K: Serialize + Ord,
- V: Serialize,
- {
- (value as &BTreeMap<_, _>).serialize(serializer)
- }
-
- pub fn deserialize<'de, D, K, V, S>(
- deserializer: D,
- ) -> Result<BoundedBTreeMap<K, V, S>, D::Error>
- where
- D: de::Deserializer<'de>,
- K: de::Deserialize<'de> + Ord,
- V: de::Deserialize<'de>,
- S: Get<u32>,
- {
- let map = <BTreeMap<K, V>>::deserialize(deserializer)?;
- let len = map.len();
- TryFrom::try_from(map).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
- }
-}
-
-fn bounded_map_debug<K, V, S>(
- v: &BoundedBTreeMap<K, V, S>,
- f: &mut fmt::Formatter,
-) -> Result<(), fmt::Error>
-where
- K: fmt::Debug + Ord,
- V: fmt::Debug,
-{
- use core::fmt::Debug;
- (&v as &BTreeMap<K, V>).fmt(f)
-}
-
-#[cfg(feature = "serde1")]
-#[allow(dead_code)]
-mod bounded_set_serde {
- use core::convert::TryFrom;
- use sp_std::collections::btree_set::BTreeSet;
- use frame_support::{traits::Get, storage::bounded_btree_set::BoundedBTreeSet};
- use serde::{
- ser::{self, Serialize},
- de::{self, Deserialize, Error},
- };
- pub fn serialize<D, K, S>(
- value: &BoundedBTreeSet<K, S>,
- serializer: D,
- ) -> Result<D::Ok, D::Error>
- where
- D: ser::Serializer,
- K: Serialize + Ord,
- {
- (value as &BTreeSet<_>).serialize(serializer)
- }
-
- pub fn deserialize<'de, D, K, S>(deserializer: D) -> Result<BoundedBTreeSet<K, S>, D::Error>
- where
- D: de::Deserializer<'de>,
- K: de::Deserialize<'de> + Ord,
- S: Get<u32>,
- {
- let map = <BTreeSet<K>>::deserialize(deserializer)?;
- let len = map.len();
- TryFrom::try_from(map).map_err(|_| D::Error::invalid_length(len, &"lesser size"))
- }
}
-fn bounded_set_debug<K, S>(
- v: &BoundedBTreeSet<K, S>,
- f: &mut fmt::Formatter,
-) -> Result<(), fmt::Error>
-where
- K: fmt::Debug + Ord,
-{
- use core::fmt::Debug;
- (&v as &BTreeSet<K>).fmt(f)
-}
-
#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative, TypeInfo)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
pub struct CreateNftData {
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub variable_data: BoundedVec<u8, CustomDataLimit>,
}
@@ -578,11 +444,11 @@
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
#[derivative(Debug)]
pub struct CreateReFungibleData {
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
- #[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[cfg_attr(feature = "serde1", serde(with = "bounded::vec_serde"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub variable_data: BoundedVec<u8, CustomDataLimit>,
pub pieces: u128,
}
@@ -612,9 +478,9 @@
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug)]
pub struct CreateNftExData<CrossAccountId> {
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub variable_data: BoundedVec<u8, CustomDataLimit>,
pub owner: CrossAccountId,
}
@@ -622,11 +488,11 @@
#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub struct CreateRefungibleExData<CrossAccountId> {
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub const_data: BoundedVec<u8, CustomDataLimit>,
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
pub variable_data: BoundedVec<u8, CustomDataLimit>,
- #[derivative(Debug(format_with = "bounded_map_debug"))]
+ #[derivative(Debug(format_with = "bounded::map_debug"))]
pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
}
@@ -634,16 +500,16 @@
#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
pub enum CreateItemExData<CrossAccountId> {
NFT(
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
),
Fungible(
- #[derivative(Debug(format_with = "bounded_map_debug"))]
+ #[derivative(Debug(format_with = "bounded::map_debug"))]
BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
),
/// Many tokens, each may have only one owner
RefungibleMultipleItems(
- #[derivative(Debug(format_with = "bounded_debug"))]
+ #[derivative(Debug(format_with = "bounded::vec_debug"))]
BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
),
/// Single token, which may have many owners