difftreelog
feat createMultipleItemsEx call
in: master
16 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -17,7 +17,7 @@
TokenId, Weight, WithdrawReasons, CollectionStats, MAX_TOKEN_OWNERSHIP, CollectionMode,
NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT, MAX_SPONSOR_TIMEOUT, CUSTOM_DATA_LIMIT, CollectionLimits,
- CustomDataLimit, CreateCollectionData, SponsorshipState,
+ CustomDataLimit, CreateCollectionData, SponsorshipState, CreateItemExData,
};
pub use pallet::*;
use sp_core::H160;
@@ -624,9 +624,10 @@
}
/// Worst cases
-pub trait CommonWeightInfo {
+pub trait CommonWeightInfo<CrossAccountId> {
fn create_item() -> Weight;
fn create_multiple_items(amount: u32) -> Weight;
+ fn create_multiple_items_ex(cost: &CreateItemExData<CrossAccountId>) -> Weight;
fn burn_item() -> Weight;
fn transfer() -> Weight;
fn approve() -> Weight;
@@ -648,6 +649,11 @@
to: T::CrossAccountId,
data: Vec<CreateItemData>,
) -> DispatchResultWithPostInfo;
+ fn create_multiple_items_ex(
+ &self,
+ sender: T::CrossAccountId,
+ data: CreateItemExData<T::CrossAccountId>,
+ ) -> DispatchResultWithPostInfo;
fn burn_item(
&self,
sender: T::CrossAccountId,
pallets/fungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/fungible/src/benchmarking.rs
+++ b/pallets/fungible/src/benchmarking.rs
@@ -4,7 +4,7 @@
use sp_std::prelude::*;
use pallet_common::benchmarking::create_collection_raw;
use frame_benchmarking::{benchmarks, account};
-use up_data_structs::{CollectionMode};
+use up_data_structs::{CollectionMode, MAX_ITEMS_PER_BATCH};
use pallet_common::bench_init;
const SEED: u32 = 1;
@@ -26,6 +26,18 @@
};
}: {<Pallet<T>>::create_item(&collection, &sender, (to, 200))?}
+ create_multiple_items_ex {
+ let b in 0..MAX_ITEMS_PER_BATCH;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ sender: cross_from_sub(owner);
+ };
+ let data = (0..b).map(|i| {
+ bench_init!(to: cross_sub(i););
+ (to, 200)
+ }).collect::<BTreeMap<_, _>>().try_into().unwrap();
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)}
+
burn_item {
bench_init!{
owner: sub; collection: collection(owner);
pallets/fungible/src/common.rsdiffbeforeafterboth--- a/pallets/fungible/src/common.rs
+++ b/pallets/fungible/src/common.rs
@@ -1,7 +1,7 @@
use core::marker::PhantomData;
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, BoundedVec};
-use up_data_structs::TokenId;
+use up_data_structs::{TokenId, CreateItemExData};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::ArithmeticError;
use sp_std::{vec::Vec, vec};
@@ -21,6 +21,15 @@
Self::create_item()
}
+ fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+ match data {
+ CreateItemExData::Fungible(f) => {
+ <SelfWeightOf<T>>::create_multiple_items_ex(f.len() as u32)
+ }
+ _ => 0,
+ }
+ }
+
fn burn_item() -> Weight {
<SelfWeightOf<T>>::burn_item()
}
@@ -87,6 +96,23 @@
)
}
+ fn create_multiple_items_ex(
+ &self,
+ sender: <T>::CrossAccountId,
+ data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,
+ ) -> DispatchResultWithPostInfo {
+ let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+ let data = match data {
+ up_data_structs::CreateItemExData::Fungible(f) => f,
+ _ => fail!(<Error<T>>::NotFungibleDataUsedToMintFungibleCollectionToken),
+ };
+
+ with_weight(
+ <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),
+ weight,
+ )
+ }
+
fn burn_item(
&self,
sender: T::CrossAccountId,
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -392,6 +392,6 @@
sender: &T::CrossAccountId,
data: CreateItemData<T>,
) -> DispatchResult {
- Self::create_multiple_items(collection, sender, vec![data])
+ Self::create_multiple_items(collection, sender, [(data.0, data.1)].into_iter().collect())
}
}
pallets/fungible/src/weights.rsdiffbeforeafterboth--- a/pallets/fungible/src/weights.rs
+++ b/pallets/fungible/src/weights.rs
@@ -33,6 +33,7 @@
/// Weight functions needed for pallet_fungible.
pub trait WeightInfo {
fn create_item() -> Weight;
+ fn create_multiple_items_ex(b: u32, ) -> Weight;
fn burn_item() -> Weight;
fn transfer() -> Weight;
fn approve() -> Weight;
@@ -51,6 +52,17 @@
.saturating_add(T::DbWeight::get().writes(2 as Weight))
}
// Storage: Fungible TotalSupply (r:1 w:1)
+ // Storage: Fungible Balance (r:4 w:4)
+ fn create_multiple_items_ex(b: u32, ) -> Weight {
+ (1_055_000 as Weight)
+ // Standard Error: 22_000
+ .saturating_add((5_273_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+ }
+ // Storage: Fungible TotalSupply (r:1 w:1)
// Storage: Fungible Balance (r:1 w:1)
fn burn_item() -> Weight {
(14_096_000 as Weight)
@@ -97,6 +109,17 @@
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
}
// Storage: Fungible TotalSupply (r:1 w:1)
+ // Storage: Fungible Balance (r:4 w:4)
+ fn create_multiple_items_ex(b: u32, ) -> Weight {
+ (1_055_000 as Weight)
+ // Standard Error: 22_000
+ .saturating_add((5_273_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(b as Weight)))
+ }
+ // Storage: Fungible TotalSupply (r:1 w:1)
// Storage: Fungible Balance (r:1 w:1)
fn burn_item() -> Weight {
(14_096_000 as Weight)
pallets/nonfungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/benchmarking.rs
+++ b/pallets/nonfungible/src/benchmarking.rs
@@ -56,6 +56,16 @@
let data = (0..b).map(|_| create_max_item_data::<T>(to.clone())).collect();
}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ create_multiple_items_ex {
+ let b in 0..MAX_ITEMS_PER_BATCH;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ sender: cross_from_sub(owner);
+ };
+ let data = (0..b).map(|i| {
+ bench_init!(to: cross_sub(i););
+ create_max_item_data::<T>(to)
+ }).collect();
}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
burn_item {
pallets/nonfungible/src/common.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/common.rs
+++ b/pallets/nonfungible/src/common.rs
@@ -1,7 +1,7 @@
use core::marker::PhantomData;
use frame_support::{dispatch::DispatchResultWithPostInfo, ensure, fail, weights::Weight, BoundedVec};
-use up_data_structs::{TokenId, CustomDataLimit};
+use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::DispatchError;
use sp_std::vec::Vec;
@@ -17,6 +17,13 @@
<SelfWeightOf<T>>::create_item()
}
+ fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+ match data {
+ CreateItemExData::NFT(t) => <SelfWeightOf<T>>::create_multiple_items_ex(t.len() as u32),
+ _ => 0,
+ }
+ }
+
fn create_multiple_items(amount: u32) -> Weight {
<SelfWeightOf<T>>::create_multiple_items(amount)
}
@@ -91,6 +98,23 @@
)
}
+ fn create_multiple_items_ex(
+ &self,
+ sender: <T>::CrossAccountId,
+ data: up_data_structs::CreateItemExData<<T>::CrossAccountId>,
+ ) -> DispatchResultWithPostInfo {
+ let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+ let data = match data {
+ up_data_structs::CreateItemExData::NFT(nft) => nft,
+ _ => fail!(Error::<T>::NotNonfungibleDataUsedToMintFungibleCollectionToken),
+ };
+
+ with_weight(
+ <Pallet<T>>::create_multiple_items(self, &sender, data.into_inner()),
+ weight,
+ )
+ }
+
fn burn_item(
&self,
sender: T::CrossAccountId,
pallets/nonfungible/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23use erc::ERC721Events;4use frame_support::{BoundedVec, ensure};5use up_data_structs::{AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData};6use pallet_common::{7 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,8};9use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};10use sp_core::H160;11use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};12use sp_std::{vec::Vec, vec};13use core::ops::Deref;14use sp_std::collections::btree_map::BTreeMap;15use codec::{Encode, Decode, MaxEncodedLen};16use scale_info::TypeInfo;1718pub use pallet::*;19#[cfg(feature = "runtime-benchmarks")]20pub mod benchmarking;21pub mod common;22pub mod erc;23pub mod weights;2425pub struct CreateItemData<T: Config> {26 pub const_data: BoundedVec<u8, CustomDataLimit>,27 pub variable_data: BoundedVec<u8, CustomDataLimit>,28 pub owner: T::CrossAccountId,29}30pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;3132#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]33pub struct ItemData<CrossAccountId> {34 pub const_data: BoundedVec<u8, CustomDataLimit>,35 pub variable_data: BoundedVec<u8, CustomDataLimit>,36 pub owner: CrossAccountId,37}3839#[frame_support::pallet]40pub mod pallet {41 use super::*;42 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};43 use up_data_structs::{CollectionId, TokenId};44 use super::weights::WeightInfo;4546 #[pallet::error]47 pub enum Error<T> {48 /// Not Nonfungible item data used to mint in Nonfungible collection.49 NotNonfungibleDataUsedToMintFungibleCollectionToken,50 /// Used amount > 1 with NFT51 NonfungibleItemsHaveNoAmount,52 }5354 #[pallet::config]55 pub trait Config: frame_system::Config + pallet_common::Config {56 type WeightInfo: WeightInfo;57 }5859 #[pallet::pallet]60 #[pallet::generate_store(pub(super) trait Store)]61 pub struct Pallet<T>(_);6263 #[pallet::storage]64 pub type TokensMinted<T: Config> =65 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;66 #[pallet::storage]67 pub type TokensBurnt<T: Config> =68 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;6970 #[pallet::storage]71 pub type TokenData<T: Config> = StorageNMap<72 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),73 Value = ItemData<T::CrossAccountId>,74 QueryKind = OptionQuery,75 >;7677 /// Used to enumerate tokens owned by account78 #[pallet::storage]79 pub type Owned<T: Config> = StorageNMap<80 Key = (81 Key<Twox64Concat, CollectionId>,82 Key<Blake2_128Concat, T::CrossAccountId>,83 Key<Twox64Concat, TokenId>,84 ),85 Value = bool,86 QueryKind = ValueQuery,87 >;8889 #[pallet::storage]90 pub type AccountBalance<T: Config> = StorageNMap<91 Key = (92 Key<Twox64Concat, CollectionId>,93 Key<Blake2_128Concat, T::CrossAccountId>,94 ),95 Value = u32,96 QueryKind = ValueQuery,97 >;9899 #[pallet::storage]100 pub type Allowance<T: Config> = StorageNMap<101 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),102 Value = T::CrossAccountId,103 QueryKind = OptionQuery,104 >;105}106107pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);108impl<T: Config> NonfungibleHandle<T> {109 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {110 Self(inner)111 }112 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {113 self.0114 }115}116impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {117 fn recorder(&self) -> &SubstrateRecorder<T> {118 self.0.recorder()119 }120 fn into_recorder(self) -> SubstrateRecorder<T> {121 self.0.into_recorder()122 }123}124impl<T: Config> Deref for NonfungibleHandle<T> {125 type Target = pallet_common::CollectionHandle<T>;126127 fn deref(&self) -> &Self::Target {128 &self.0129 }130}131132impl<T: Config> Pallet<T> {133 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {134 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)135 }136 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {137 <TokenData<T>>::contains_key((collection.id, token))138 }139}140141// unchecked calls skips any permission checks142impl<T: Config> Pallet<T> {143 pub fn init_collection(144 owner: T::AccountId,145 data: CreateCollectionData<T::AccountId>,146 ) -> Result<CollectionId, DispatchError> {147 <PalletCommon<T>>::init_collection(owner, data)148 }149 pub fn destroy_collection(150 collection: NonfungibleHandle<T>,151 sender: &T::CrossAccountId,152 ) -> DispatchResult {153 let id = collection.id;154155 // =========156157 PalletCommon::destroy_collection(collection.0, sender)?;158159 <TokenData<T>>::remove_prefix((id,), None);160 <Owned<T>>::remove_prefix((id,), None);161 <TokensMinted<T>>::remove(id);162 <TokensBurnt<T>>::remove(id);163 <Allowance<T>>::remove_prefix((id,), None);164 <AccountBalance<T>>::remove_prefix((id,), None);165 Ok(())166 }167168 pub fn burn(169 collection: &NonfungibleHandle<T>,170 sender: &T::CrossAccountId,171 token: TokenId,172 ) -> DispatchResult {173 let token_data =174 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;175 ensure!(176 &token_data.owner == sender177 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),178 <CommonError<T>>::NoPermission179 );180181 if collection.access == AccessMode::AllowList {182 collection.check_allowlist(sender)?;183 }184185 let burnt = <TokensBurnt<T>>::get(collection.id)186 .checked_add(1)187 .ok_or(ArithmeticError::Overflow)?;188189 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))190 .checked_sub(1)191 .ok_or(ArithmeticError::Overflow)?;192193 if balance == 0 {194 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));195 } else {196 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);197 }198 // =========199200 <Owned<T>>::remove((collection.id, &token_data.owner, token));201 <TokensBurnt<T>>::insert(collection.id, burnt);202 <TokenData<T>>::remove((collection.id, token));203 let old_spender = <Allowance<T>>::take((collection.id, token));204205 if let Some(old_spender) = old_spender {206 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(207 collection.id,208 token,209 sender.clone(),210 old_spender,211 0,212 ));213 }214215 collection.log_mirrored(ERC721Events::Transfer {216 from: *token_data.owner.as_eth(),217 to: H160::default(),218 token_id: token.into(),219 });220 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(221 collection.id,222 token,223 token_data.owner,224 1,225 ));226 Ok(())227 }228229 pub fn transfer(230 collection: &NonfungibleHandle<T>,231 from: &T::CrossAccountId,232 to: &T::CrossAccountId,233 token: TokenId,234 ) -> DispatchResult {235 ensure!(236 collection.limits.transfers_enabled(),237 <CommonError<T>>::TransferNotAllowed238 );239240 let token_data =241 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;242 ensure!(243 &token_data.owner == from244 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),245 <CommonError<T>>::NoPermission246 );247248 if collection.access == AccessMode::AllowList {249 collection.check_allowlist(from)?;250 collection.check_allowlist(to)?;251 }252 <PalletCommon<T>>::ensure_correct_receiver(to)?;253254 let balance_from = <AccountBalance<T>>::get((collection.id, from))255 .checked_sub(1)256 .ok_or(<CommonError<T>>::TokenValueTooLow)?;257 let balance_to = if from != to {258 let balance_to = <AccountBalance<T>>::get((collection.id, to))259 .checked_add(1)260 .ok_or(ArithmeticError::Overflow)?;261262 ensure!(263 balance_to < collection.limits.account_token_ownership_limit(),264 <CommonError<T>>::AccountTokenLimitExceeded,265 );266267 Some(balance_to)268 } else {269 None270 };271272 // =========273274 <TokenData<T>>::insert(275 (collection.id, token),276 ItemData {277 owner: to.clone(),278 ..token_data279 },280 );281282 if let Some(balance_to) = balance_to {283 // from != to284 if balance_from == 0 {285 <AccountBalance<T>>::remove((collection.id, from));286 } else {287 <AccountBalance<T>>::insert((collection.id, from), balance_from);288 }289 <AccountBalance<T>>::insert((collection.id, to), balance_to);290 <Owned<T>>::remove((collection.id, from, token));291 <Owned<T>>::insert((collection.id, to, token), true);292 }293 Self::set_allowance_unchecked(collection, from, token, None, true);294295 collection.log_mirrored(ERC721Events::Transfer {296 from: *from.as_eth(),297 to: *to.as_eth(),298 token_id: token.into(),299 });300 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(301 collection.id,302 token,303 from.clone(),304 to.clone(),305 1,306 ));307 Ok(())308 }309310 pub fn create_multiple_items(311 collection: &NonfungibleHandle<T>,312 sender: &T::CrossAccountId,313 data: Vec<CreateItemData<T>>,314 ) -> DispatchResult {315 if !collection.is_owner_or_admin(sender) {316 ensure!(317 collection.mint_mode,318 <CommonError<T>>::PublicMintingNotAllowed319 );320 collection.check_allowlist(sender)?;321322 for item in data.iter() {323 collection.check_allowlist(&item.owner)?;324 }325 }326327 for data in data.iter() {328 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;329 }330331 let first_token = <TokensMinted<T>>::get(collection.id);332 let tokens_minted = first_token333 .checked_add(data.len() as u32)334 .ok_or(ArithmeticError::Overflow)?;335 ensure!(336 tokens_minted <= collection.limits.token_limit(),337 <CommonError<T>>::CollectionTokenLimitExceeded338 );339340 let mut balances = BTreeMap::new();341 for data in &data {342 let balance = balances343 .entry(&data.owner)344 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));345 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;346347 ensure!(348 *balance <= collection.limits.account_token_ownership_limit(),349 <CommonError<T>>::AccountTokenLimitExceeded,350 );351 }352353 // =========354355 <TokensMinted<T>>::insert(collection.id, tokens_minted);356 for (account, balance) in balances {357 <AccountBalance<T>>::insert((collection.id, account), balance);358 }359 for (i, data) in data.into_iter().enumerate() {360 let token = first_token + i as u32 + 1;361362 <TokenData<T>>::insert(363 (collection.id, token),364 ItemData {365 const_data: data.const_data,366 variable_data: data.variable_data,367 owner: data.owner.clone(),368 },369 );370 <Owned<T>>::insert((collection.id, &data.owner, token), true);371372 collection.log_mirrored(ERC721Events::Transfer {373 from: H160::default(),374 to: *data.owner.as_eth(),375 token_id: token.into(),376 });377 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(378 collection.id,379 TokenId(token),380 data.owner.clone(),381 1,382 ));383 }384 Ok(())385 }386387 pub fn set_allowance_unchecked(388 collection: &NonfungibleHandle<T>,389 sender: &T::CrossAccountId,390 token: TokenId,391 spender: Option<&T::CrossAccountId>,392 assume_implicit_eth: bool,393 ) {394 if let Some(spender) = spender {395 let old_spender = <Allowance<T>>::get((collection.id, token));396 <Allowance<T>>::insert((collection.id, token), spender);397 // In ERC721 there is only one possible approved user of token, so we set398 // approved user to spender399 collection.log_mirrored(ERC721Events::Approval {400 owner: *sender.as_eth(),401 approved: *spender.as_eth(),402 token_id: token.into(),403 });404 // In Unique chain, any token can have any amount of approved users, so we need to405 // set allowance of old owner to 0, and allowance of new owner to 1406 if old_spender.as_ref() != Some(spender) {407 if let Some(old_owner) = old_spender {408 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(409 collection.id,410 token,411 sender.clone(),412 old_owner,413 0,414 ));415 }416 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(417 collection.id,418 token,419 sender.clone(),420 spender.clone(),421 1,422 ));423 }424 } else {425 let old_spender = <Allowance<T>>::take((collection.id, token));426 if !assume_implicit_eth {427 // In ERC721 there is only one possible approved user of token, so we set428 // approved user to zero address429 collection.log_mirrored(ERC721Events::Approval {430 owner: *sender.as_eth(),431 approved: H160::default(),432 token_id: token.into(),433 });434 }435 // In Unique chain, any token can have any amount of approved users, so we need to436 // set allowance of old owner to 0437 if let Some(old_spender) = old_spender {438 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(439 collection.id,440 token,441 sender.clone(),442 old_spender,443 0,444 ));445 }446 }447 }448449 pub fn set_allowance(450 collection: &NonfungibleHandle<T>,451 sender: &T::CrossAccountId,452 token: TokenId,453 spender: Option<&T::CrossAccountId>,454 ) -> DispatchResult {455 if collection.access == AccessMode::AllowList {456 collection.check_allowlist(sender)?;457 if let Some(spender) = spender {458 collection.check_allowlist(spender)?;459 }460 }461462 if let Some(spender) = spender {463 <PalletCommon<T>>::ensure_correct_receiver(spender)?;464 }465 let token_data =466 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;467 if &token_data.owner != sender {468 ensure!(469 collection.ignores_owned_amount(sender),470 <CommonError<T>>::CantApproveMoreThanOwned471 );472 }473474 // =========475476 Self::set_allowance_unchecked(collection, sender, token, spender, false);477 Ok(())478 }479480 pub fn transfer_from(481 collection: &NonfungibleHandle<T>,482 spender: &T::CrossAccountId,483 from: &T::CrossAccountId,484 to: &T::CrossAccountId,485 token: TokenId,486 ) -> DispatchResult {487 if spender.conv_eq(from) {488 return Self::transfer(collection, from, to, token);489 }490 if collection.access == AccessMode::AllowList {491 // `from`, `to` checked in [`transfer`]492 collection.check_allowlist(spender)?;493 }494495 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {496 ensure!(497 collection.ignores_allowance(spender),498 <CommonError<T>>::ApprovedValueTooLow499 );500 }501502 // =========503504 Self::transfer(collection, from, to, token)?;505 // Allowance is reset in [`transfer`]506 Ok(())507 }508509 pub fn burn_from(510 collection: &NonfungibleHandle<T>,511 spender: &T::CrossAccountId,512 from: &T::CrossAccountId,513 token: TokenId,514 ) -> DispatchResult {515 if spender.conv_eq(from) {516 return Self::burn(collection, from, token);517 }518 if collection.access == AccessMode::AllowList {519 // `from` checked in [`burn`]520 collection.check_allowlist(spender)?;521 }522523 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {524 ensure!(525 collection.ignores_allowance(spender),526 <CommonError<T>>::ApprovedValueTooLow527 );528 }529530 // =========531532 Self::burn(collection, from, token)533 }534535 pub fn set_variable_metadata(536 collection: &NonfungibleHandle<T>,537 sender: &T::CrossAccountId,538 token: TokenId,539 data: BoundedVec<u8, CustomDataLimit>,540 ) -> DispatchResult {541 let token_data =542 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;543 collection.check_can_update_meta(sender, &token_data.owner)?;544545 // =========546547 <TokenData<T>>::insert(548 (collection.id, token),549 ItemData {550 variable_data: data,551 ..token_data552 },553 );554 Ok(())555 }556557 /// Delegated to `create_multiple_items`558 pub fn create_item(559 collection: &NonfungibleHandle<T>,560 sender: &T::CrossAccountId,561 data: CreateItemData<T>,562 ) -> DispatchResult {563 Self::create_multiple_items(collection, sender, vec![data])564 }565}1#![cfg_attr(not(feature = "std"), no_std)]23use erc::ERC721Events;4use frame_support::{BoundedVec, ensure};5use up_data_structs::{6 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,7};8use pallet_common::{9 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,10};11use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};12use sp_core::H160;13use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};14use sp_std::{vec::Vec, vec};15use core::ops::Deref;16use sp_std::collections::btree_map::BTreeMap;17use codec::{Encode, Decode, MaxEncodedLen};18use scale_info::TypeInfo;1920pub use pallet::*;21#[cfg(feature = "runtime-benchmarks")]22pub mod benchmarking;23pub mod common;24pub mod erc;25pub mod weights;2627pub type CreateItemData<T> = CreateNftExData<<T as pallet_common::Config>::CrossAccountId>;28pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;2930#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]31pub struct ItemData<CrossAccountId> {32 pub const_data: BoundedVec<u8, CustomDataLimit>,33 pub variable_data: BoundedVec<u8, CustomDataLimit>,34 pub owner: CrossAccountId,35}3637#[frame_support::pallet]38pub mod pallet {39 use super::*;40 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};41 use up_data_structs::{CollectionId, TokenId};42 use super::weights::WeightInfo;4344 #[pallet::error]45 pub enum Error<T> {46 /// Not Nonfungible item data used to mint in Nonfungible collection.47 NotNonfungibleDataUsedToMintFungibleCollectionToken,48 /// Used amount > 1 with NFT49 NonfungibleItemsHaveNoAmount,50 }5152 #[pallet::config]53 pub trait Config: frame_system::Config + pallet_common::Config {54 type WeightInfo: WeightInfo;55 }5657 #[pallet::pallet]58 #[pallet::generate_store(pub(super) trait Store)]59 pub struct Pallet<T>(_);6061 #[pallet::storage]62 pub type TokensMinted<T: Config> =63 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;64 #[pallet::storage]65 pub type TokensBurnt<T: Config> =66 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;6768 #[pallet::storage]69 pub type TokenData<T: Config> = StorageNMap<70 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),71 Value = ItemData<T::CrossAccountId>,72 QueryKind = OptionQuery,73 >;7475 /// Used to enumerate tokens owned by account76 #[pallet::storage]77 pub type Owned<T: Config> = StorageNMap<78 Key = (79 Key<Twox64Concat, CollectionId>,80 Key<Blake2_128Concat, T::CrossAccountId>,81 Key<Twox64Concat, TokenId>,82 ),83 Value = bool,84 QueryKind = ValueQuery,85 >;8687 #[pallet::storage]88 pub type AccountBalance<T: Config> = StorageNMap<89 Key = (90 Key<Twox64Concat, CollectionId>,91 Key<Blake2_128Concat, T::CrossAccountId>,92 ),93 Value = u32,94 QueryKind = ValueQuery,95 >;9697 #[pallet::storage]98 pub type Allowance<T: Config> = StorageNMap<99 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),100 Value = T::CrossAccountId,101 QueryKind = OptionQuery,102 >;103}104105pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);106impl<T: Config> NonfungibleHandle<T> {107 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {108 Self(inner)109 }110 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {111 self.0112 }113}114impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {115 fn recorder(&self) -> &SubstrateRecorder<T> {116 self.0.recorder()117 }118 fn into_recorder(self) -> SubstrateRecorder<T> {119 self.0.into_recorder()120 }121}122impl<T: Config> Deref for NonfungibleHandle<T> {123 type Target = pallet_common::CollectionHandle<T>;124125 fn deref(&self) -> &Self::Target {126 &self.0127 }128}129130impl<T: Config> Pallet<T> {131 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {132 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)133 }134 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {135 <TokenData<T>>::contains_key((collection.id, token))136 }137}138139// unchecked calls skips any permission checks140impl<T: Config> Pallet<T> {141 pub fn init_collection(142 owner: T::AccountId,143 data: CreateCollectionData<T::AccountId>,144 ) -> Result<CollectionId, DispatchError> {145 <PalletCommon<T>>::init_collection(owner, data)146 }147 pub fn destroy_collection(148 collection: NonfungibleHandle<T>,149 sender: &T::CrossAccountId,150 ) -> DispatchResult {151 let id = collection.id;152153 // =========154155 PalletCommon::destroy_collection(collection.0, sender)?;156157 <TokenData<T>>::remove_prefix((id,), None);158 <Owned<T>>::remove_prefix((id,), None);159 <TokensMinted<T>>::remove(id);160 <TokensBurnt<T>>::remove(id);161 <Allowance<T>>::remove_prefix((id,), None);162 <AccountBalance<T>>::remove_prefix((id,), None);163 Ok(())164 }165166 pub fn burn(167 collection: &NonfungibleHandle<T>,168 sender: &T::CrossAccountId,169 token: TokenId,170 ) -> DispatchResult {171 let token_data =172 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;173 ensure!(174 &token_data.owner == sender175 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),176 <CommonError<T>>::NoPermission177 );178179 if collection.access == AccessMode::AllowList {180 collection.check_allowlist(sender)?;181 }182183 let burnt = <TokensBurnt<T>>::get(collection.id)184 .checked_add(1)185 .ok_or(ArithmeticError::Overflow)?;186187 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))188 .checked_sub(1)189 .ok_or(ArithmeticError::Overflow)?;190191 if balance == 0 {192 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));193 } else {194 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);195 }196 // =========197198 <Owned<T>>::remove((collection.id, &token_data.owner, token));199 <TokensBurnt<T>>::insert(collection.id, burnt);200 <TokenData<T>>::remove((collection.id, token));201 let old_spender = <Allowance<T>>::take((collection.id, token));202203 if let Some(old_spender) = old_spender {204 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(205 collection.id,206 token,207 sender.clone(),208 old_spender,209 0,210 ));211 }212213 collection.log_mirrored(ERC721Events::Transfer {214 from: *token_data.owner.as_eth(),215 to: H160::default(),216 token_id: token.into(),217 });218 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(219 collection.id,220 token,221 token_data.owner,222 1,223 ));224 Ok(())225 }226227 pub fn transfer(228 collection: &NonfungibleHandle<T>,229 from: &T::CrossAccountId,230 to: &T::CrossAccountId,231 token: TokenId,232 ) -> DispatchResult {233 ensure!(234 collection.limits.transfers_enabled(),235 <CommonError<T>>::TransferNotAllowed236 );237238 let token_data =239 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;240 ensure!(241 &token_data.owner == from242 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),243 <CommonError<T>>::NoPermission244 );245246 if collection.access == AccessMode::AllowList {247 collection.check_allowlist(from)?;248 collection.check_allowlist(to)?;249 }250 <PalletCommon<T>>::ensure_correct_receiver(to)?;251252 let balance_from = <AccountBalance<T>>::get((collection.id, from))253 .checked_sub(1)254 .ok_or(<CommonError<T>>::TokenValueTooLow)?;255 let balance_to = if from != to {256 let balance_to = <AccountBalance<T>>::get((collection.id, to))257 .checked_add(1)258 .ok_or(ArithmeticError::Overflow)?;259260 ensure!(261 balance_to < collection.limits.account_token_ownership_limit(),262 <CommonError<T>>::AccountTokenLimitExceeded,263 );264265 Some(balance_to)266 } else {267 None268 };269270 // =========271272 <TokenData<T>>::insert(273 (collection.id, token),274 ItemData {275 owner: to.clone(),276 ..token_data277 },278 );279280 if let Some(balance_to) = balance_to {281 // from != to282 if balance_from == 0 {283 <AccountBalance<T>>::remove((collection.id, from));284 } else {285 <AccountBalance<T>>::insert((collection.id, from), balance_from);286 }287 <AccountBalance<T>>::insert((collection.id, to), balance_to);288 <Owned<T>>::remove((collection.id, from, token));289 <Owned<T>>::insert((collection.id, to, token), true);290 }291 Self::set_allowance_unchecked(collection, from, token, None, true);292293 collection.log_mirrored(ERC721Events::Transfer {294 from: *from.as_eth(),295 to: *to.as_eth(),296 token_id: token.into(),297 });298 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(299 collection.id,300 token,301 from.clone(),302 to.clone(),303 1,304 ));305 Ok(())306 }307308 pub fn create_multiple_items(309 collection: &NonfungibleHandle<T>,310 sender: &T::CrossAccountId,311 data: Vec<CreateItemData<T>>,312 ) -> DispatchResult {313 if !collection.is_owner_or_admin(sender) {314 ensure!(315 collection.mint_mode,316 <CommonError<T>>::PublicMintingNotAllowed317 );318 collection.check_allowlist(sender)?;319320 for item in data.iter() {321 collection.check_allowlist(&item.owner)?;322 }323 }324325 for data in data.iter() {326 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;327 }328329 let first_token = <TokensMinted<T>>::get(collection.id);330 let tokens_minted = first_token331 .checked_add(data.len() as u32)332 .ok_or(ArithmeticError::Overflow)?;333 ensure!(334 tokens_minted <= collection.limits.token_limit(),335 <CommonError<T>>::CollectionTokenLimitExceeded336 );337338 let mut balances = BTreeMap::new();339 for data in &data {340 let balance = balances341 .entry(&data.owner)342 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));343 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;344345 ensure!(346 *balance <= collection.limits.account_token_ownership_limit(),347 <CommonError<T>>::AccountTokenLimitExceeded,348 );349 }350351 // =========352353 <TokensMinted<T>>::insert(collection.id, tokens_minted);354 for (account, balance) in balances {355 <AccountBalance<T>>::insert((collection.id, account), balance);356 }357 for (i, data) in data.into_iter().enumerate() {358 let token = first_token + i as u32 + 1;359360 <TokenData<T>>::insert(361 (collection.id, token),362 ItemData {363 const_data: data.const_data,364 variable_data: data.variable_data,365 owner: data.owner.clone(),366 },367 );368 <Owned<T>>::insert((collection.id, &data.owner, token), true);369370 collection.log_mirrored(ERC721Events::Transfer {371 from: H160::default(),372 to: *data.owner.as_eth(),373 token_id: token.into(),374 });375 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(376 collection.id,377 TokenId(token),378 data.owner.clone(),379 1,380 ));381 }382 Ok(())383 }384385 pub fn set_allowance_unchecked(386 collection: &NonfungibleHandle<T>,387 sender: &T::CrossAccountId,388 token: TokenId,389 spender: Option<&T::CrossAccountId>,390 assume_implicit_eth: bool,391 ) {392 if let Some(spender) = spender {393 let old_spender = <Allowance<T>>::get((collection.id, token));394 <Allowance<T>>::insert((collection.id, token), spender);395 // In ERC721 there is only one possible approved user of token, so we set396 // approved user to spender397 collection.log_mirrored(ERC721Events::Approval {398 owner: *sender.as_eth(),399 approved: *spender.as_eth(),400 token_id: token.into(),401 });402 // In Unique chain, any token can have any amount of approved users, so we need to403 // set allowance of old owner to 0, and allowance of new owner to 1404 if old_spender.as_ref() != Some(spender) {405 if let Some(old_owner) = old_spender {406 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(407 collection.id,408 token,409 sender.clone(),410 old_owner,411 0,412 ));413 }414 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(415 collection.id,416 token,417 sender.clone(),418 spender.clone(),419 1,420 ));421 }422 } else {423 let old_spender = <Allowance<T>>::take((collection.id, token));424 if !assume_implicit_eth {425 // In ERC721 there is only one possible approved user of token, so we set426 // approved user to zero address427 collection.log_mirrored(ERC721Events::Approval {428 owner: *sender.as_eth(),429 approved: H160::default(),430 token_id: token.into(),431 });432 }433 // In Unique chain, any token can have any amount of approved users, so we need to434 // set allowance of old owner to 0435 if let Some(old_spender) = old_spender {436 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(437 collection.id,438 token,439 sender.clone(),440 old_spender,441 0,442 ));443 }444 }445 }446447 pub fn set_allowance(448 collection: &NonfungibleHandle<T>,449 sender: &T::CrossAccountId,450 token: TokenId,451 spender: Option<&T::CrossAccountId>,452 ) -> DispatchResult {453 if collection.access == AccessMode::AllowList {454 collection.check_allowlist(sender)?;455 if let Some(spender) = spender {456 collection.check_allowlist(spender)?;457 }458 }459460 if let Some(spender) = spender {461 <PalletCommon<T>>::ensure_correct_receiver(spender)?;462 }463 let token_data =464 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;465 if &token_data.owner != sender {466 ensure!(467 collection.ignores_owned_amount(sender),468 <CommonError<T>>::CantApproveMoreThanOwned469 );470 }471472 // =========473474 Self::set_allowance_unchecked(collection, sender, token, spender, false);475 Ok(())476 }477478 pub fn transfer_from(479 collection: &NonfungibleHandle<T>,480 spender: &T::CrossAccountId,481 from: &T::CrossAccountId,482 to: &T::CrossAccountId,483 token: TokenId,484 ) -> DispatchResult {485 if spender.conv_eq(from) {486 return Self::transfer(collection, from, to, token);487 }488 if collection.access == AccessMode::AllowList {489 // `from`, `to` checked in [`transfer`]490 collection.check_allowlist(spender)?;491 }492493 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {494 ensure!(495 collection.ignores_allowance(spender),496 <CommonError<T>>::ApprovedValueTooLow497 );498 }499500 // =========501502 Self::transfer(collection, from, to, token)?;503 // Allowance is reset in [`transfer`]504 Ok(())505 }506507 pub fn burn_from(508 collection: &NonfungibleHandle<T>,509 spender: &T::CrossAccountId,510 from: &T::CrossAccountId,511 token: TokenId,512 ) -> DispatchResult {513 if spender.conv_eq(from) {514 return Self::burn(collection, from, token);515 }516 if collection.access == AccessMode::AllowList {517 // `from` checked in [`burn`]518 collection.check_allowlist(spender)?;519 }520521 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {522 ensure!(523 collection.ignores_allowance(spender),524 <CommonError<T>>::ApprovedValueTooLow525 );526 }527528 // =========529530 Self::burn(collection, from, token)531 }532533 pub fn set_variable_metadata(534 collection: &NonfungibleHandle<T>,535 sender: &T::CrossAccountId,536 token: TokenId,537 data: BoundedVec<u8, CustomDataLimit>,538 ) -> DispatchResult {539 let token_data =540 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;541 collection.check_can_update_meta(sender, &token_data.owner)?;542543 // =========544545 <TokenData<T>>::insert(546 (collection.id, token),547 ItemData {548 variable_data: data,549 ..token_data550 },551 );552 Ok(())553 }554555 /// Delegated to `create_multiple_items`556 pub fn create_item(557 collection: &NonfungibleHandle<T>,558 sender: &T::CrossAccountId,559 data: CreateItemData<T>,560 ) -> DispatchResult {561 Self::create_multiple_items(collection, sender, vec![data])562 }563}pallets/nonfungible/src/weights.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/weights.rs
+++ b/pallets/nonfungible/src/weights.rs
@@ -34,6 +34,7 @@
pub trait WeightInfo {
fn create_item() -> Weight;
fn create_multiple_items(b: u32, ) -> Weight;
+ fn create_multiple_items_ex(b: u32, ) -> Weight;
fn burn_item() -> Weight;
fn transfer() -> Weight;
fn approve() -> Weight;
@@ -66,6 +67,19 @@
.saturating_add(T::DbWeight::get().writes(2 as Weight))
.saturating_add(T::DbWeight::get().writes((2 as Weight).saturating_mul(b as Weight)))
}
+ // Storage: Nonfungible TokensMinted (r:1 w:1)
+ // Storage: Nonfungible AccountBalance (r:4 w:4)
+ // Storage: Nonfungible TokenData (r:0 w:4)
+ // Storage: Nonfungible Owned (r:0 w:4)
+ fn create_multiple_items_ex(b: u32, ) -> Weight {
+ (2_090_000 as Weight)
+ // Standard Error: 10_000
+ .saturating_add((9_230_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+ }
// Storage: Nonfungible TokenData (r:1 w:1)
// Storage: Nonfungible TokensBurnt (r:1 w:1)
// Storage: Nonfungible Allowance (r:1 w:0)
@@ -140,6 +154,19 @@
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
.saturating_add(RocksDbWeight::get().writes((2 as Weight).saturating_mul(b as Weight)))
}
+ // Storage: Nonfungible TokensMinted (r:1 w:1)
+ // Storage: Nonfungible AccountBalance (r:4 w:4)
+ // Storage: Nonfungible TokenData (r:0 w:4)
+ // Storage: Nonfungible Owned (r:0 w:4)
+ fn create_multiple_items_ex(b: u32, ) -> Weight {
+ (2_090_000 as Weight)
+ // Standard Error: 10_000
+ .saturating_add((9_230_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+ }
// Storage: Nonfungible TokenData (r:1 w:1)
// Storage: Nonfungible TokensBurnt (r:1 w:1)
// Storage: Nonfungible Allowance (r:1 w:0)
pallets/refungible/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/refungible/src/benchmarking.rs
+++ b/pallets/refungible/src/benchmarking.rs
@@ -31,7 +31,8 @@
sender: &T::CrossAccountId,
users: impl IntoIterator<Item = (T::CrossAccountId, u128)>,
) -> Result<TokenId, DispatchError> {
- <Pallet<T>>::create_item(&collection, sender, create_max_item_data(users))?;
+ let data: CreateRefungibleExData<T::CrossAccountId> = create_max_item_data(users);
+ <Pallet<T>>::create_item(&collection, sender, data)?;
Ok(TokenId(<TokensMinted<T>>::get(&collection.id)))
}
@@ -60,6 +61,30 @@
let data = (0..b).map(|_| create_max_item_data([(to.clone(), 200)])).collect();
}: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+ create_multiple_items_ex_multiple_items {
+ let b in 0..MAX_ITEMS_PER_BATCH;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ sender: cross_from_sub(owner);
+ };
+ let data = (0..b).map(|t| {
+ bench_init!(to: cross_sub(t););
+ create_max_item_data([(to, 200)])
+ }).collect();
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+
+ create_multiple_items_ex_multiple_owners {
+ let b in 0..MAX_ITEMS_PER_BATCH;
+ bench_init!{
+ owner: sub; collection: collection(owner);
+ sender: cross_from_sub(owner);
+ };
+ let data = vec![create_max_item_data((0..b).map(|u| {
+ bench_init!(to: cross_sub(u););
+ (to, 200)
+ }))].try_into().unwrap();
+ }: {<Pallet<T>>::create_multiple_items(&collection, &sender, data)?}
+
// Other user left, token data is kept
burn_item_partial {
bench_init!{
@@ -170,6 +195,6 @@
sender: cross_from_sub(owner);
};
let item = create_max_item(&collection, &sender, [(sender.clone(), 200)])?;
- let data = create_data(b as usize);
+ let data = create_var_data(b).try_into().unwrap();
}: {<Pallet<T>>::set_variable_metadata(&collection, &sender, item, data)?}
}
pallets/refungible/src/common.rsdiffbeforeafterboth--- a/pallets/refungible/src/common.rs
+++ b/pallets/refungible/src/common.rs
@@ -2,10 +2,10 @@
use sp_std::collections::btree_map::BTreeMap;
use frame_support::{dispatch::DispatchResultWithPostInfo, fail, weights::Weight, BoundedVec};
-use up_data_structs::{TokenId, CustomDataLimit};
+use up_data_structs::{TokenId, CustomDataLimit, CreateItemExData, CreateRefungibleExData};
use pallet_common::{CommonCollectionOperations, CommonWeightInfo, with_weight};
use sp_runtime::DispatchError;
-use sp_std::vec::Vec;
+use sp_std::{vec::Vec, vec};
use crate::{
AccountBalance, Allowance, Balance, Config, Error, Owned, Pallet, RefungibleHandle,
@@ -31,6 +31,18 @@
<SelfWeightOf<T>>::create_multiple_items(amount)
}
+ fn create_multiple_items_ex(call: &CreateItemExData<T::CrossAccountId>) -> Weight {
+ match call {
+ CreateItemExData::RefungibleMultipleOwners(i) => {
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_owners(i.users.len() as u32)
+ }
+ CreateItemExData::RefungibleMultipleItems(i) => {
+ <SelfWeightOf<T>>::create_multiple_items_ex_multiple_items(i.len() as u32)
+ }
+ _ => 0,
+ }
+ }
+
fn burn_item() -> Weight {
max_weight_of!(burn_item_partial(), burn_item_fully())
}
@@ -69,15 +81,15 @@
fn map_create_data<T: Config>(
data: up_data_structs::CreateItemData,
to: &T::CrossAccountId,
-) -> Result<CreateItemData<T>, DispatchError> {
+) -> Result<CreateRefungibleExData<T::CrossAccountId>, DispatchError> {
match data {
- up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateItemData {
+ up_data_structs::CreateItemData::ReFungible(data) => Ok(CreateRefungibleExData {
const_data: data.const_data,
variable_data: data.variable_data,
users: {
let mut out = BTreeMap::new();
out.insert(to.clone(), data.pieces);
- out
+ out.try_into().expect("limit > 0")
},
}),
_ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),
@@ -92,7 +104,7 @@
data: up_data_structs::CreateItemData,
) -> DispatchResultWithPostInfo {
with_weight(
- <Pallet<T>>::create_item(self, &sender, map_create_data(data, &to)?),
+ <Pallet<T>>::create_item(self, &sender, map_create_data::<T>(data, &to)?),
<CommonWeights<T>>::create_item(),
)
}
@@ -115,6 +127,28 @@
)
}
+ fn create_multiple_items_ex(
+ &self,
+ sender: <T>::CrossAccountId,
+ data: CreateItemExData<T::CrossAccountId>,
+ ) -> DispatchResultWithPostInfo {
+ let weight = <CommonWeights<T>>::create_multiple_items_ex(&data);
+ let data = match data {
+ CreateItemExData::RefungibleMultipleOwners(r) => vec![r],
+ CreateItemExData::RefungibleMultipleItems(r)
+ if r.iter().all(|i| i.users.len() == 1) =>
+ {
+ r.into_inner()
+ }
+ _ => fail!(<Error<T>>::NotRefungibleDataUsedToMintFungibleCollectionToken),
+ };
+
+ with_weight(
+ <Pallet<T>>::create_multiple_items(self, &sender, data),
+ weight,
+ )
+ }
+
fn burn_item(
&self,
sender: T::CrossAccountId,
pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -2,7 +2,8 @@
use frame_support::{ensure, BoundedVec};
use up_data_structs::{
- AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId, CreateCollectionData,
+ AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
+ CreateCollectionData, CreateRefungibleExData,
};
use pallet_common::{
Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,
@@ -19,11 +20,6 @@
pub mod common;
pub mod erc;
pub mod weights;
-pub struct CreateItemData<T: Config> {
- pub const_data: BoundedVec<u8, CustomDataLimit>,
- pub variable_data: BoundedVec<u8, CustomDataLimit>,
- pub users: BTreeMap<T::CrossAccountId, u128>,
-}
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
#[derive(Encode, Decode, Default, TypeInfo, MaxEncodedLen)]
@@ -361,7 +357,7 @@
pub fn create_multiple_items(
collection: &RefungibleHandle<T>,
sender: &T::CrossAccountId,
- data: Vec<CreateItemData<T>>,
+ data: Vec<CreateRefungibleExData<T::CrossAccountId>>,
) -> DispatchResult {
if !collection.is_owner_or_admin(sender) {
ensure!(
@@ -606,7 +602,7 @@
pub fn create_item(
collection: &RefungibleHandle<T>,
sender: &T::CrossAccountId,
- data: CreateItemData<T>,
+ data: CreateRefungibleExData<T::CrossAccountId>,
) -> DispatchResult {
Self::create_multiple_items(collection, sender, vec![data])
}
pallets/refungible/src/weights.rsdiffbeforeafterboth--- a/pallets/refungible/src/weights.rs
+++ b/pallets/refungible/src/weights.rs
@@ -34,6 +34,8 @@
pub trait WeightInfo {
fn create_item() -> Weight;
fn create_multiple_items(b: u32, ) -> Weight;
+ fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight;
+ fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight;
fn burn_item_partial() -> Weight;
fn burn_item_fully() -> Weight;
fn transfer_normal() -> Weight;
@@ -77,6 +79,36 @@
.saturating_add(T::DbWeight::get().writes(2 as Weight))
.saturating_add(T::DbWeight::get().writes((4 as Weight).saturating_mul(b as Weight)))
}
+ // Storage: Refungible TokensMinted (r:1 w:1)
+ // Storage: Refungible AccountBalance (r:4 w:4)
+ // Storage: Refungible Balance (r:0 w:4)
+ // Storage: Refungible TotalSupply (r:0 w:4)
+ // Storage: Refungible TokenData (r:0 w:4)
+ // Storage: Refungible Owned (r:0 w:4)
+ fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight {
+ (11_953_000 as Weight)
+ // Standard Error: 27_000
+ .saturating_add((10_775_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes((5 as Weight).saturating_mul(b as Weight)))
+ }
+ // Storage: Refungible TokensMinted (r:1 w:1)
+ // Storage: Refungible TotalSupply (r:0 w:1)
+ // Storage: Refungible TokenData (r:0 w:1)
+ // Storage: Refungible AccountBalance (r:4 w:4)
+ // Storage: Refungible Balance (r:0 w:4)
+ // Storage: Refungible Owned (r:0 w:4)
+ fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 13_000
+ .saturating_add((8_528_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(T::DbWeight::get().writes(3 as Weight))
+ .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+ }
// Storage: Refungible TotalSupply (r:1 w:1)
// Storage: Refungible Balance (r:1 w:1)
// Storage: Refungible AccountBalance (r:1 w:1)
@@ -215,6 +247,36 @@
.saturating_add(RocksDbWeight::get().writes(2 as Weight))
.saturating_add(RocksDbWeight::get().writes((4 as Weight).saturating_mul(b as Weight)))
}
+ // Storage: Refungible TokensMinted (r:1 w:1)
+ // Storage: Refungible AccountBalance (r:4 w:4)
+ // Storage: Refungible Balance (r:0 w:4)
+ // Storage: Refungible TotalSupply (r:0 w:4)
+ // Storage: Refungible TokenData (r:0 w:4)
+ // Storage: Refungible Owned (r:0 w:4)
+ fn create_multiple_items_ex_multiple_items(b: u32, ) -> Weight {
+ (11_953_000 as Weight)
+ // Standard Error: 27_000
+ .saturating_add((10_775_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes((5 as Weight).saturating_mul(b as Weight)))
+ }
+ // Storage: Refungible TokensMinted (r:1 w:1)
+ // Storage: Refungible TotalSupply (r:0 w:1)
+ // Storage: Refungible TokenData (r:0 w:1)
+ // Storage: Refungible AccountBalance (r:4 w:4)
+ // Storage: Refungible Balance (r:0 w:4)
+ // Storage: Refungible Owned (r:0 w:4)
+ fn create_multiple_items_ex_multiple_owners(b: u32, ) -> Weight {
+ (0 as Weight)
+ // Standard Error: 13_000
+ .saturating_add((8_528_000 as Weight).saturating_mul(b as Weight))
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(b as Weight)))
+ .saturating_add(RocksDbWeight::get().writes(3 as Weight))
+ .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(b as Weight)))
+ }
// Storage: Refungible TotalSupply (r:1 w:1)
// Storage: Refungible Balance (r:1 w:1)
// Storage: Refungible AccountBalance (r:1 w:1)
pallets/unique/src/common.rsdiffbeforeafterboth--- a/pallets/unique/src/common.rs
+++ b/pallets/unique/src/common.rs
@@ -5,6 +5,7 @@
use pallet_fungible::{common::CommonWeights as FungibleWeights};
use pallet_nonfungible::{common::CommonWeights as NonfungibleWeights};
use pallet_refungible::{common::CommonWeights as RefungibleWeights};
+use up_data_structs::CreateItemExData;
use crate::{Config, dispatch::dispatch_weight};
@@ -17,7 +18,7 @@
}
pub struct CommonWeights<T: Config>(PhantomData<T>);
-impl<T: Config> CommonWeightInfo for CommonWeights<T> {
+impl<T: Config> CommonWeightInfo<T::CrossAccountId> for CommonWeights<T> {
fn create_item() -> up_data_structs::Weight {
dispatch_weight::<T>() + max_weight_of!(create_item())
}
@@ -26,6 +27,10 @@
dispatch_weight::<T>() + max_weight_of!(create_multiple_items(amount))
}
+ fn create_multiple_items_ex(data: &CreateItemExData<T::CrossAccountId>) -> Weight {
+ dispatch_weight::<T>() + max_weight_of!(create_multiple_items_ex(data))
+ }
+
fn burn_item() -> Weight {
dispatch_weight::<T>() + max_weight_of!(burn_item())
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -40,7 +40,7 @@
OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,
MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,
CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
- CreateCollectionData, CustomDataLimit,
+ CreateCollectionData, CustomDataLimit, CreateItemExData,
};
use pallet_common::{
account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,
@@ -735,6 +735,14 @@
dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))
}
+ #[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]
+ #[transactional]
+ pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {
+ let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);
+
+ dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))
+ }
+
// TODO! transaction weight
/// Set transfers_enabled value for particular collection
primitives/data-structs/src/lib.rsdiffbeforeafterboth--- a/primitives/data-structs/src/lib.rs
+++ b/primitives/data-structs/src/lib.rs
@@ -1,6 +1,11 @@
#![cfg_attr(not(feature = "std"), no_std)]
-use core::convert::{TryFrom, TryInto};
+use core::{
+ convert::{TryFrom, TryInto},
+ fmt,
+};
+use frame_support::storage::bounded_btree_map::BoundedBTreeMap;
+use sp_std::collections::btree_map::BTreeMap;
#[cfg(feature = "serde")]
pub use serde::{Serialize, Deserialize};
@@ -525,6 +530,47 @@
ReFungible(CreateReFungibleData),
}
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
+#[derivative(Debug)]
+pub struct CreateNftExData<CrossAccountId> {
+ #[derivative(Debug(format_with = "bounded_debug"))]
+ pub const_data: BoundedVec<u8, CustomDataLimit>,
+ #[derivative(Debug(format_with = "bounded_debug"))]
+ pub variable_data: BoundedVec<u8, CustomDataLimit>,
+ pub owner: CrossAccountId,
+}
+
+#[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"))]
+ pub const_data: BoundedVec<u8, CustomDataLimit>,
+ #[derivative(Debug(format_with = "bounded_debug"))]
+ pub variable_data: BoundedVec<u8, CustomDataLimit>,
+ #[derivative(Debug(format_with = "bounded_map_debug"))]
+ pub users: BoundedBTreeMap<CrossAccountId, u128, ConstU32<MAX_ITEMS_PER_BATCH>>,
+}
+
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, TypeInfo, Derivative)]
+#[derivative(Debug(bound = "CrossAccountId: fmt::Debug + Ord"))]
+pub enum CreateItemExData<CrossAccountId> {
+ NFT(
+ #[derivative(Debug(format_with = "bounded_debug"))]
+ BoundedVec<CreateNftExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
+ ),
+ Fungible(
+ #[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"))]
+ BoundedVec<CreateRefungibleExData<CrossAccountId>, ConstU32<MAX_ITEMS_PER_BATCH>>,
+ ),
+ /// Single token, which may have many owners
+ RefungibleMultipleOwners(CreateRefungibleExData<CrossAccountId>),
+}
+
impl CreateItemData {
pub fn data_size(&self) -> usize {
match self {