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.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/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, CustomDataLimit, CreateItemExData};
+use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData, CollectionId};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::DispatchError;
use sp_std::vec::Vec;
@@ -237,6 +237,15 @@
)
}
+ fn nest_token(
+ &self,
+ sender: T::CrossAccountId,
+ (from, _): (CollectionId, TokenId),
+ under: TokenId,
+ ) -> sp_runtime::DispatchResult {
+ <Pallet<T>>::nest_token(self, sender, from, under)
+ }
+
fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId> {
<Owned<T>>::iter_prefix((self.id, account))
.map(|(id, _)| id)
pallets/nonfungible/src/lib.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/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use erc::ERC721Events;20use frame_support::{BoundedVec, ensure};21use up_data_structs::{22 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,23};24use pallet_common::{Error as CommonError, Pallet as PalletCommon, Event as CommonEvent};25use pallet_evm::account::CrossAccountId;26use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};27use sp_core::H160;28use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};29use sp_std::{vec::Vec, vec};30use core::ops::Deref;31use sp_std::collections::btree_map::BTreeMap;32use codec::{Encode, Decode, MaxEncodedLen};33use scale_info::TypeInfo;3435pub use pallet::*;36#[cfg(feature = "runtime-benchmarks")]37pub mod benchmarking;38pub mod common;39pub mod erc;40pub mod weights;4142pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;43pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4445#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]46pub struct ItemData<CrossAccountId> {47 pub const_data: BoundedVec<u8, CustomDataLimit>,48 pub variable_data: BoundedVec<u8, CustomDataLimit>,49 pub owner: CrossAccountId,50}5152#[frame_support::pallet]53pub mod pallet {54 use super::*;55 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};56 use up_data_structs::{CollectionId, TokenId};57 use super::weights::WeightInfo;5859 #[pallet::error]60 pub enum Error<T> {61 /// Not Nonfungible item data used to mint in Nonfungible collection.62 NotNonfungibleDataUsedToMintFungibleCollectionToken,63 /// Used amount > 1 with NFT64 NonfungibleItemsHaveNoAmount,65 }6667 #[pallet::config]68 pub trait Config: frame_system::Config + pallet_common::Config {69 type WeightInfo: WeightInfo;70 }7172 #[pallet::pallet]73 #[pallet::generate_store(pub(super) trait Store)]74 pub struct Pallet<T>(_);7576 #[pallet::storage]77 pub type TokensMinted<T: Config> =78 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;79 #[pallet::storage]80 pub type TokensBurnt<T: Config> =81 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;8283 #[pallet::storage]84 pub type TokenData<T: Config> = StorageNMap<85 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),86 Value = ItemData<T::CrossAccountId>,87 QueryKind = OptionQuery,88 >;8990 /// Used to enumerate tokens owned by account91 #[pallet::storage]92 pub type Owned<T: Config> = StorageNMap<93 Key = (94 Key<Twox64Concat, CollectionId>,95 Key<Blake2_128Concat, T::CrossAccountId>,96 Key<Twox64Concat, TokenId>,97 ),98 Value = bool,99 QueryKind = ValueQuery,100 >;101102 #[pallet::storage]103 pub type AccountBalance<T: Config> = StorageNMap<104 Key = (105 Key<Twox64Concat, CollectionId>,106 Key<Blake2_128Concat, T::CrossAccountId>,107 ),108 Value = u32,109 QueryKind = ValueQuery,110 >;111112 #[pallet::storage]113 pub type Allowance<T: Config> = StorageNMap<114 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),115 Value = T::CrossAccountId,116 QueryKind = OptionQuery,117 >;118}119120pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);121impl<T: Config> NonfungibleHandle<T> {122 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {123 Self(inner)124 }125 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {126 self.0127 }128}129impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {130 fn recorder(&self) -> &SubstrateRecorder<T> {131 self.0.recorder()132 }133 fn into_recorder(self) -> SubstrateRecorder<T> {134 self.0.into_recorder()135 }136}137impl<T: Config> Deref for NonfungibleHandle<T> {138 type Target = pallet_common::CollectionHandle<T>;139140 fn deref(&self) -> &Self::Target {141 &self.0142 }143}144145impl<T: Config> Pallet<T> {146 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {147 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)148 }149 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {150 <TokenData<T>>::contains_key((collection.id, token))151 }152}153154// unchecked calls skips any permission checks155impl<T: Config> Pallet<T> {156 pub fn init_collection(157 owner: T::AccountId,158 data: CreateCollectionData<T::AccountId>,159 ) -> Result<CollectionId, DispatchError> {160 <PalletCommon<T>>::init_collection(owner, data)161 }162 pub fn destroy_collection(163 collection: NonfungibleHandle<T>,164 sender: &T::CrossAccountId,165 ) -> DispatchResult {166 let id = collection.id;167168 // =========169170 PalletCommon::destroy_collection(collection.0, sender)?;171172 <TokenData<T>>::remove_prefix((id,), None);173 <Owned<T>>::remove_prefix((id,), None);174 <TokensMinted<T>>::remove(id);175 <TokensBurnt<T>>::remove(id);176 <Allowance<T>>::remove_prefix((id,), None);177 <AccountBalance<T>>::remove_prefix((id,), None);178 Ok(())179 }180181 pub fn burn(182 collection: &NonfungibleHandle<T>,183 sender: &T::CrossAccountId,184 token: TokenId,185 ) -> DispatchResult {186 let token_data =187 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;188 ensure!(189 &token_data.owner == sender190 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),191 <CommonError<T>>::NoPermission192 );193194 if collection.access == AccessMode::AllowList {195 collection.check_allowlist(sender)?;196 }197198 let burnt = <TokensBurnt<T>>::get(collection.id)199 .checked_add(1)200 .ok_or(ArithmeticError::Overflow)?;201202 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))203 .checked_sub(1)204 .ok_or(ArithmeticError::Overflow)?;205206 if balance == 0 {207 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));208 } else {209 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);210 }211 // =========212213 <Owned<T>>::remove((collection.id, &token_data.owner, token));214 <TokensBurnt<T>>::insert(collection.id, burnt);215 <TokenData<T>>::remove((collection.id, token));216 let old_spender = <Allowance<T>>::take((collection.id, token));217218 if let Some(old_spender) = old_spender {219 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(220 collection.id,221 token,222 sender.clone(),223 old_spender,224 0,225 ));226 }227228 collection.log_mirrored(ERC721Events::Transfer {229 from: *token_data.owner.as_eth(),230 to: H160::default(),231 token_id: token.into(),232 });233 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(234 collection.id,235 token,236 token_data.owner,237 1,238 ));239 Ok(())240 }241242 pub fn transfer(243 collection: &NonfungibleHandle<T>,244 from: &T::CrossAccountId,245 to: &T::CrossAccountId,246 token: TokenId,247 ) -> DispatchResult {248 ensure!(249 collection.limits.transfers_enabled(),250 <CommonError<T>>::TransferNotAllowed251 );252253 let token_data =254 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;255 ensure!(256 &token_data.owner == from257 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),258 <CommonError<T>>::NoPermission259 );260261 if collection.access == AccessMode::AllowList {262 collection.check_allowlist(from)?;263 collection.check_allowlist(to)?;264 }265 <PalletCommon<T>>::ensure_correct_receiver(to)?;266267 let balance_from = <AccountBalance<T>>::get((collection.id, from))268 .checked_sub(1)269 .ok_or(<CommonError<T>>::TokenValueTooLow)?;270 let balance_to = if from != to {271 let balance_to = <AccountBalance<T>>::get((collection.id, to))272 .checked_add(1)273 .ok_or(ArithmeticError::Overflow)?;274275 ensure!(276 balance_to < collection.limits.account_token_ownership_limit(),277 <CommonError<T>>::AccountTokenLimitExceeded,278 );279280 Some(balance_to)281 } else {282 None283 };284285 // =========286287 <TokenData<T>>::insert(288 (collection.id, token),289 ItemData {290 owner: to.clone(),291 ..token_data292 },293 );294295 if let Some(balance_to) = balance_to {296 // from != to297 if balance_from == 0 {298 <AccountBalance<T>>::remove((collection.id, from));299 } else {300 <AccountBalance<T>>::insert((collection.id, from), balance_from);301 }302 <AccountBalance<T>>::insert((collection.id, to), balance_to);303 <Owned<T>>::remove((collection.id, from, token));304 <Owned<T>>::insert((collection.id, to, token), true);305 }306 Self::set_allowance_unchecked(collection, from, token, None, true);307308 collection.log_mirrored(ERC721Events::Transfer {309 from: *from.as_eth(),310 to: *to.as_eth(),311 token_id: token.into(),312 });313 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(314 collection.id,315 token,316 from.clone(),317 to.clone(),318 1,319 ));320 Ok(())321 }322323 pub fn create_multiple_items(324 collection: &NonfungibleHandle<T>,325 sender: &T::CrossAccountId,326 data: Vec<CreateItemData<T>>,327 ) -> DispatchResult {328 if !collection.is_owner_or_admin(sender) {329 ensure!(330 collection.mint_mode,331 <CommonError<T>>::PublicMintingNotAllowed332 );333 collection.check_allowlist(sender)?;334335 for item in data.iter() {336 collection.check_allowlist(&item.owner)?;337 }338 }339340 for data in data.iter() {341 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;342 }343344 let first_token = <TokensMinted<T>>::get(collection.id);345 let tokens_minted = first_token346 .checked_add(data.len() as u32)347 .ok_or(ArithmeticError::Overflow)?;348 ensure!(349 tokens_minted <= collection.limits.token_limit(),350 <CommonError<T>>::CollectionTokenLimitExceeded351 );352353 let mut balances = BTreeMap::new();354 for data in &data {355 let balance = balances356 .entry(&data.owner)357 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));358 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;359360 ensure!(361 *balance <= collection.limits.account_token_ownership_limit(),362 <CommonError<T>>::AccountTokenLimitExceeded,363 );364 }365366 // =========367368 <TokensMinted<T>>::insert(collection.id, tokens_minted);369 for (account, balance) in balances {370 <AccountBalance<T>>::insert((collection.id, account), balance);371 }372 for (i, data) in data.into_iter().enumerate() {373 let token = first_token + i as u32 + 1;374375 <TokenData<T>>::insert(376 (collection.id, token),377 ItemData {378 const_data: data.const_data,379 variable_data: data.variable_data,380 owner: data.owner.clone(),381 },382 );383 <Owned<T>>::insert((collection.id, &data.owner, token), true);384385 collection.log_mirrored(ERC721Events::Transfer {386 from: H160::default(),387 to: *data.owner.as_eth(),388 token_id: token.into(),389 });390 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(391 collection.id,392 TokenId(token),393 data.owner.clone(),394 1,395 ));396 }397 Ok(())398 }399400 pub fn set_allowance_unchecked(401 collection: &NonfungibleHandle<T>,402 sender: &T::CrossAccountId,403 token: TokenId,404 spender: Option<&T::CrossAccountId>,405 assume_implicit_eth: bool,406 ) {407 if let Some(spender) = spender {408 let old_spender = <Allowance<T>>::get((collection.id, token));409 <Allowance<T>>::insert((collection.id, token), spender);410 // In ERC721 there is only one possible approved user of token, so we set411 // approved user to spender412 collection.log_mirrored(ERC721Events::Approval {413 owner: *sender.as_eth(),414 approved: *spender.as_eth(),415 token_id: token.into(),416 });417 // In Unique chain, any token can have any amount of approved users, so we need to418 // set allowance of old owner to 0, and allowance of new owner to 1419 if old_spender.as_ref() != Some(spender) {420 if let Some(old_owner) = old_spender {421 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(422 collection.id,423 token,424 sender.clone(),425 old_owner,426 0,427 ));428 }429 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(430 collection.id,431 token,432 sender.clone(),433 spender.clone(),434 1,435 ));436 }437 } else {438 let old_spender = <Allowance<T>>::take((collection.id, token));439 if !assume_implicit_eth {440 // In ERC721 there is only one possible approved user of token, so we set441 // approved user to zero address442 collection.log_mirrored(ERC721Events::Approval {443 owner: *sender.as_eth(),444 approved: H160::default(),445 token_id: token.into(),446 });447 }448 // In Unique chain, any token can have any amount of approved users, so we need to449 // set allowance of old owner to 0450 if let Some(old_spender) = old_spender {451 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(452 collection.id,453 token,454 sender.clone(),455 old_spender,456 0,457 ));458 }459 }460 }461462 pub fn set_allowance(463 collection: &NonfungibleHandle<T>,464 sender: &T::CrossAccountId,465 token: TokenId,466 spender: Option<&T::CrossAccountId>,467 ) -> DispatchResult {468 if collection.access == AccessMode::AllowList {469 collection.check_allowlist(sender)?;470 if let Some(spender) = spender {471 collection.check_allowlist(spender)?;472 }473 }474475 if let Some(spender) = spender {476 <PalletCommon<T>>::ensure_correct_receiver(spender)?;477 }478 let token_data =479 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;480 if &token_data.owner != sender {481 ensure!(482 collection.ignores_owned_amount(sender),483 <CommonError<T>>::CantApproveMoreThanOwned484 );485 }486487 // =========488489 Self::set_allowance_unchecked(collection, sender, token, spender, false);490 Ok(())491 }492493 pub fn transfer_from(494 collection: &NonfungibleHandle<T>,495 spender: &T::CrossAccountId,496 from: &T::CrossAccountId,497 to: &T::CrossAccountId,498 token: TokenId,499 ) -> DispatchResult {500 if spender.conv_eq(from) {501 return Self::transfer(collection, from, to, token);502 }503 if collection.access == AccessMode::AllowList {504 // `from`, `to` checked in [`transfer`]505 collection.check_allowlist(spender)?;506 }507508 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {509 ensure!(510 collection.ignores_allowance(spender),511 <CommonError<T>>::ApprovedValueTooLow512 );513 }514515 // =========516517 Self::transfer(collection, from, to, token)?;518 // Allowance is reset in [`transfer`]519 Ok(())520 }521522 pub fn burn_from(523 collection: &NonfungibleHandle<T>,524 spender: &T::CrossAccountId,525 from: &T::CrossAccountId,526 token: TokenId,527 ) -> DispatchResult {528 if spender.conv_eq(from) {529 return Self::burn(collection, from, token);530 }531 if collection.access == AccessMode::AllowList {532 // `from` checked in [`burn`]533 collection.check_allowlist(spender)?;534 }535536 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {537 ensure!(538 collection.ignores_allowance(spender),539 <CommonError<T>>::ApprovedValueTooLow540 );541 }542543 // =========544545 Self::burn(collection, from, token)546 }547548 pub fn set_variable_metadata(549 collection: &NonfungibleHandle<T>,550 sender: &T::CrossAccountId,551 token: TokenId,552 data: BoundedVec<u8, CustomDataLimit>,553 ) -> DispatchResult {554 let token_data =555 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;556 collection.check_can_update_meta(sender, &token_data.owner)?;557558 // =========559560 <TokenData<T>>::insert(561 (collection.id, token),562 ItemData {563 variable_data: data,564 ..token_data565 },566 );567 Ok(())568 }569570 /// Delegated to `create_multiple_items`571 pub fn create_item(572 collection: &NonfungibleHandle<T>,573 sender: &T::CrossAccountId,574 data: CreateItemData<T>,575 ) -> DispatchResult {576 Self::create_multiple_items(collection, sender, vec![data])577 }578}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