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.rsdiffbeforeafterboth--- a/pallets/nonfungible/src/lib.rs
+++ b/pallets/nonfungible/src/lib.rs
@@ -2,7 +2,9 @@
use erc::ERC721Events;
use frame_support::{BoundedVec, ensure};
-use up_data_structs::{AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData};
+use up_data_structs::{
+ AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,
+};
use pallet_common::{
Error as CommonError, Pallet as PalletCommon, Event as CommonEvent, account::CrossAccountId,
};
@@ -22,11 +24,7 @@
pub mod erc;
pub mod weights;
-pub struct CreateItemData<T: Config> {
- pub const_data: BoundedVec<u8, CustomDataLimit>,
- pub variable_data: BoundedVec<u8, CustomDataLimit>,
- pub owner: T::CrossAccountId,
-}
+pub type CreateItemData<T> = CreateNftExData<<T as pallet_common::Config>::CrossAccountId>;
pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;
#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]
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.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error, decl_event,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},33 BoundedVec,34};35use scale_info::TypeInfo;36use frame_system::{self as system, ensure_signed};37use sp_runtime::{sp_std::prelude::Vec};38use up_data_structs::{39 MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,40 OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,41 MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,43 CreateCollectionData, CustomDataLimit,44};45use pallet_common::{46 account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,47 CommonWeightInfo,48};49use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};50use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};51use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5253#[cfg(test)]54mod mock;5556#[cfg(test)]57mod tests;5859mod eth;60mod sponsorship;61pub use sponsorship::UniqueSponsorshipHandler;62pub use eth::sponsoring::UniqueEthSponsorshipHandler;6364pub use eth::UniqueErcSupport;6566pub mod common;67use common::CommonWeights;68pub mod dispatch;69use dispatch::dispatch_call;7071#[cfg(feature = "runtime-benchmarks")]72mod benchmarking;73pub mod weights;74use weights::WeightInfo;7576decl_error! {77 /// Error for non-fungible-token module.78 pub enum Error for Module<T: Config> {79 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.80 CollectionDecimalPointLimitExceeded,81 /// This address is not set as sponsor, use setCollectionSponsor first.82 ConfirmUnsetSponsorFail,83 /// Length of items properties must be greater than 0.84 EmptyArgument,85 }86}8788pub trait Config:89 system::Config90 + pallet_evm_coder_substrate::Config91 + pallet_common::Config92 + pallet_nonfungible::Config93 + pallet_refungible::Config94 + pallet_fungible::Config95 + Sized96 + TypeInfo97{98 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;99100 /// Weight information for extrinsics in this pallet.101 type WeightInfo: WeightInfo;102}103104decl_event! {105 pub enum Event<T>106 where107 <T as frame_system::Config>::AccountId,108 <T as pallet_common::Config>::CrossAccountId,109 {110 /// Collection sponsor was removed111 ///112 /// # Arguments113 ///114 /// * collection_id: Globally unique collection identifier.115 CollectionSponsorRemoved(CollectionId),116117 /// Collection admin was added118 ///119 /// # Arguments120 ///121 /// * collection_id: Globally unique collection identifier.122 ///123 /// * admin: Admin address.124 CollectionAdminAdded(CollectionId, CrossAccountId),125126 /// Collection owned was change127 ///128 /// # Arguments129 ///130 /// * collection_id: Globally unique collection identifier.131 ///132 /// * owner: New owner address.133 CollectionOwnedChanged(CollectionId, AccountId),134135 /// Collection sponsor was set136 ///137 /// # Arguments138 ///139 /// * collection_id: Globally unique collection identifier.140 ///141 /// * owner: New sponsor address.142 CollectionSponsorSet(CollectionId, AccountId),143144 /// const on chain schema was set145 ///146 /// # Arguments147 ///148 /// * collection_id: Globally unique collection identifier.149 ConstOnChainSchemaSet(CollectionId),150151 /// New sponsor was confirm152 ///153 /// # Arguments154 ///155 /// * collection_id: Globally unique collection identifier.156 ///157 /// * sponsor: New sponsor address.158 SponsorshipConfirmed(CollectionId, AccountId),159160 /// Collection admin was removed161 ///162 /// # Arguments163 ///164 /// * collection_id: Globally unique collection identifier.165 ///166 /// * admin: Admin address.167 CollectionAdminRemoved(CollectionId, CrossAccountId),168169 /// Address was remove from allow list170 ///171 /// # Arguments172 ///173 /// * collection_id: Globally unique collection identifier.174 ///175 /// * user: Address.176 AllowListAddressRemoved(CollectionId, CrossAccountId),177178 /// Address was add to allow list179 ///180 /// # Arguments181 ///182 /// * collection_id: Globally unique collection identifier.183 ///184 /// * user: Address.185 AllowListAddressAdded(CollectionId, CrossAccountId),186187 /// Collection limits was set188 ///189 /// # Arguments190 ///191 /// * collection_id: Globally unique collection identifier.192 CollectionLimitSet(CollectionId),193194 /// Mint permission was set195 ///196 /// # Arguments197 ///198 /// * collection_id: Globally unique collection identifier.199 MintPermissionSet(CollectionId),200201 /// Offchain schema was set202 ///203 /// # Arguments204 ///205 /// * collection_id: Globally unique collection identifier.206 OffchainSchemaSet(CollectionId),207208 /// Public access mode was set209 ///210 /// # Arguments211 ///212 /// * collection_id: Globally unique collection identifier.213 ///214 /// * mode: New access state.215 PublicAccessModeSet(CollectionId, AccessMode),216217 /// Schema version was set218 ///219 /// # Arguments220 ///221 /// * collection_id: Globally unique collection identifier.222 SchemaVersionSet(CollectionId),223224 /// Variable on chain schema was set225 ///226 /// # Arguments227 ///228 /// * collection_id: Globally unique collection identifier.229 VariableOnChainSchemaSet(CollectionId),230 }231}232233type SelfWeightOf<T> = <T as Config>::WeightInfo;234235// # Used definitions236//237// ## User control levels238//239// chain-controlled - key is uncontrolled by user240// i.e autoincrementing index241// can use non-cryptographic hash242// real - key is controlled by user243// but it is hard to generate enough colliding values, i.e owner of signed txs244// can use non-cryptographic hash245// controlled - key is completly controlled by users246// i.e maps with mutable keys247// should use cryptographic hash248//249// ## User control level downgrade reasons250//251// ?1 - chain-controlled -> controlled252// collections/tokens can be destroyed, resulting in massive holes253// ?2 - chain-controlled -> controlled254// same as ?1, but can be only added, resulting in easier exploitation255// ?3 - real -> controlled256// no confirmation required, so addresses can be easily generated257decl_storage! {258 trait Store for Module<T: Config> as Unique {259260 //#region Private members261 /// Used for migrations262 ChainVersion: u64;263 //#endregion264265 //#region Tokens transfer rate limit baskets266 /// (Collection id (controlled?2), who created (real))267 /// TODO: Off chain worker should remove from this map when collection gets removed268 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;269 /// Collection id (controlled?2), token id (controlled?2)270 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;271 /// Collection id (controlled?2), owning user (real)272 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;273 /// Collection id (controlled?2), token id (controlled?2)274 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;275 //#endregion276277 /// Variable metadata sponsoring278 /// Collection id (controlled?2), token id (controlled?2)279 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;280 /// Approval sponsoring281 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;282 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;283 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;284 }285}286287decl_module! {288 pub struct Module<T: Config> for enum Call289 where290 origin: T::Origin291 {292 type Error = Error<T>;293294 fn deposit_event() = default;295296 fn on_initialize(_now: T::BlockNumber) -> Weight {297 0298 }299300 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.301 ///302 /// # Permissions303 ///304 /// * Anyone.305 ///306 /// # Arguments307 ///308 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.309 ///310 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.311 ///312 /// * token_prefix: UTF-8 string with token prefix.313 ///314 /// * mode: [CollectionMode] collection type and type dependent data.315 // returns collection ID316 #[weight = <SelfWeightOf<T>>::create_collection()]317 #[transactional]318 #[deprecated]319 pub fn create_collection(origin,320 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,321 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,322 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,323 mode: CollectionMode) -> DispatchResult {324 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {325 name: collection_name,326 description: collection_description,327 token_prefix,328 mode,329 ..Default::default()330 };331 Self::create_collection_ex(origin, data)332 }333334 /// This method creates a collection335 ///336 /// Prefer it to deprecated [`created_collection`] method337 #[weight = <SelfWeightOf<T>>::create_collection()]338 #[transactional]339 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {340 let owner = ensure_signed(origin)?;341342 let _id = match data.mode {343 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},344 CollectionMode::Fungible(decimal_points) => {345 // check params346 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);347 <PalletFungible<T>>::init_collection(owner, data)?348 }349 CollectionMode::ReFungible => {350 <PalletRefungible<T>>::init_collection(owner, data)?351 }352 };353354 Ok(())355 }356357 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.358 ///359 /// # Permissions360 ///361 /// * Collection Owner.362 ///363 /// # Arguments364 ///365 /// * collection_id: collection to destroy.366 #[weight = <SelfWeightOf<T>>::destroy_collection()]367 #[transactional]368 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {369 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);370371 let collection = <CollectionHandle<T>>::try_get(collection_id)?;372 collection.check_is_owner(&sender)?;373374 // =========375376 match collection.mode {377 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,378 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,379 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,380 }381382 <NftTransferBasket<T>>::remove_prefix(collection_id, None);383 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);384 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);385386 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);387 <NftApproveBasket<T>>::remove_prefix(collection_id, None);388 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);389 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);390391 Ok(())392 }393394 /// Add an address to allow list.395 ///396 /// # Permissions397 ///398 /// * Collection Owner399 /// * Collection Admin400 ///401 /// # Arguments402 ///403 /// * collection_id.404 ///405 /// * address.406 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]407 #[transactional]408 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{409410 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);411 let collection = <CollectionHandle<T>>::try_get(collection_id)?;412413 <PalletCommon<T>>::toggle_allowlist(414 &collection,415 &sender,416 &address,417 true,418 )?;419420 Self::deposit_event(Event::<T>::AllowListAddressAdded(421 collection_id,422 address423 ));424425 Ok(())426 }427428 /// Remove an address from allow list.429 ///430 /// # Permissions431 ///432 /// * Collection Owner433 /// * Collection Admin434 ///435 /// # Arguments436 ///437 /// * collection_id.438 ///439 /// * address.440 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]441 #[transactional]442 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{443444 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);445 let collection = <CollectionHandle<T>>::try_get(collection_id)?;446447 <PalletCommon<T>>::toggle_allowlist(448 &collection,449 &sender,450 &address,451 false,452 )?;453454 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(455 collection_id,456 address457 ));458459 Ok(())460 }461462 /// Toggle between normal and allow list access for the methods with access for `Anyone`.463 ///464 /// # Permissions465 ///466 /// * Collection Owner.467 ///468 /// # Arguments469 ///470 /// * collection_id.471 ///472 /// * mode: [AccessMode]473 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]474 #[transactional]475 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult476 {477 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);478479 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;480 target_collection.check_is_owner(&sender)?;481482 target_collection.access = mode.clone();483484 <Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(485 collection_id,486 mode487 ));488489 target_collection.save()490 }491492 /// Allows Anyone to create tokens if:493 /// * Allow List is enabled, and494 /// * Address is added to allow list, and495 /// * This method was called with True parameter496 ///497 /// # Permissions498 /// * Collection Owner499 ///500 /// # Arguments501 ///502 /// * collection_id.503 ///504 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.505 #[weight = <SelfWeightOf<T>>::set_mint_permission()]506 #[transactional]507 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult508 {509 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);510511 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;512 target_collection.check_is_owner(&sender)?;513514 target_collection.mint_mode = mint_permission;515516 <Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(517 collection_id518 ));519520 target_collection.save()521 }522523 /// Change the owner of the collection.524 ///525 /// # Permissions526 ///527 /// * Collection Owner.528 ///529 /// # Arguments530 ///531 /// * collection_id.532 ///533 /// * new_owner.534 #[weight = <SelfWeightOf<T>>::change_collection_owner()]535 #[transactional]536 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {537538 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);539540 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;541 target_collection.check_is_owner(&sender)?;542543 target_collection.owner = new_owner.clone();544 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(545 collection_id,546 new_owner547 ));548549 target_collection.save()550 }551552 /// Adds an admin of the Collection.553 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.554 ///555 /// # Permissions556 ///557 /// * Collection Owner.558 /// * Collection Admin.559 ///560 /// # Arguments561 ///562 /// * collection_id: ID of the Collection to add admin for.563 ///564 /// * new_admin_id: Address of new admin to add.565 #[weight = <SelfWeightOf<T>>::add_collection_admin()]566 #[transactional]567 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {568 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);569 let collection = <CollectionHandle<T>>::try_get(collection_id)?;570571 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(572 collection_id,573 new_admin_id.clone()574 ));575576 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)577 }578579 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.580 ///581 /// # Permissions582 ///583 /// * Collection Owner.584 /// * Collection Admin.585 ///586 /// # Arguments587 ///588 /// * collection_id: ID of the Collection to remove admin for.589 ///590 /// * account_id: Address of admin to remove.591 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]592 #[transactional]593 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {594 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);595 let collection = <CollectionHandle<T>>::try_get(collection_id)?;596597 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(598 collection_id,599 account_id.clone()600 ));601602 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)603 }604605 /// # Permissions606 ///607 /// * Collection Owner608 ///609 /// # Arguments610 ///611 /// * collection_id.612 ///613 /// * new_sponsor.614 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]615 #[transactional]616 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {617 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);618619 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;620 target_collection.check_is_owner(&sender)?;621622 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());623624 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(625 collection_id,626 new_sponsor627 ));628629 target_collection.save()630 }631632 /// # Permissions633 ///634 /// * Sponsor.635 ///636 /// # Arguments637 ///638 /// * collection_id.639 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]640 #[transactional]641 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {642 let sender = ensure_signed(origin)?;643644 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;645 ensure!(646 target_collection.sponsorship.pending_sponsor() == Some(&sender),647 Error::<T>::ConfirmUnsetSponsorFail648 );649650 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());651652 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(653 collection_id,654 sender655 ));656657 target_collection.save()658 }659660 /// Switch back to pay-per-own-transaction model.661 ///662 /// # Permissions663 ///664 /// * Collection owner.665 ///666 /// # Arguments667 ///668 /// * collection_id.669 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]670 #[transactional]671 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673674 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;675 target_collection.check_is_owner(&sender)?;676677 target_collection.sponsorship = SponsorshipState::Disabled;678679 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(680 collection_id681 ));682 target_collection.save()683 }684685 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.686 ///687 /// # Permissions688 ///689 /// * Collection Owner.690 /// * Collection Admin.691 /// * Anyone if692 /// * Allow List is enabled, and693 /// * Address is added to allow list, and694 /// * MintPermission is enabled (see SetMintPermission method)695 ///696 /// # Arguments697 ///698 /// * collection_id: ID of the collection.699 ///700 /// * owner: Address, initial owner of the NFT.701 ///702 /// * data: Token data to store on chain.703 #[weight = <CommonWeights<T>>::create_item()]704 #[transactional]705 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {706 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);707708 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))709 }710711 /// This method creates multiple items in a collection created with CreateCollection method.712 ///713 /// # Permissions714 ///715 /// * Collection Owner.716 /// * Collection Admin.717 /// * Anyone if718 /// * Allow List is enabled, and719 /// * Address is added to allow list, and720 /// * MintPermission is enabled (see SetMintPermission method)721 ///722 /// # Arguments723 ///724 /// * collection_id: ID of the collection.725 ///726 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].727 ///728 /// * owner: Address, initial owner of the NFT.729 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]730 #[transactional]731 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {732 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);733 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);734735 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))736 }737738 // TODO! transaction weight739740 /// Set transfers_enabled value for particular collection741 ///742 /// # Permissions743 ///744 /// * Collection Owner.745 ///746 /// # Arguments747 ///748 /// * collection_id: ID of the collection.749 ///750 /// * value: New flag value.751 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]752 #[transactional]753 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {754 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);755 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;756 target_collection.check_is_owner(&sender)?;757758 // =========759760 target_collection.limits.transfers_enabled = Some(value);761 target_collection.save()762 }763764 /// Destroys a concrete instance of NFT.765 ///766 /// # Permissions767 ///768 /// * Collection Owner.769 /// * Collection Admin.770 /// * Current NFT Owner.771 ///772 /// # Arguments773 ///774 /// * collection_id: ID of the collection.775 ///776 /// * item_id: ID of NFT to burn.777 #[weight = <CommonWeights<T>>::burn_item()]778 #[transactional]779 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {780 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);781782 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;783 if value == 1 {784 <NftTransferBasket<T>>::remove(collection_id, item_id);785 <NftApproveBasket<T>>::remove(collection_id, item_id);786 }787 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?788 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());789 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));790 Ok(post_info)791 }792793 /// Destroys a concrete instance of NFT on behalf of the owner794 /// See also: [`approve`]795 ///796 /// # Permissions797 ///798 /// * Collection Owner.799 /// * Collection Admin.800 /// * Current NFT Owner.801 ///802 /// # Arguments803 ///804 /// * collection_id: ID of the collection.805 ///806 /// * item_id: ID of NFT to burn.807 ///808 /// * from: owner of item809 #[weight = <CommonWeights<T>>::burn_from()]810 #[transactional]811 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {812 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);813814 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))815 }816817 /// Change ownership of the token.818 ///819 /// # Permissions820 ///821 /// * Collection Owner822 /// * Collection Admin823 /// * Current NFT owner824 ///825 /// # Arguments826 ///827 /// * recipient: Address of token recipient.828 ///829 /// * collection_id.830 ///831 /// * item_id: ID of the item832 /// * Non-Fungible Mode: Required.833 /// * Fungible Mode: Ignored.834 /// * Re-Fungible Mode: Required.835 ///836 /// * value: Amount to transfer.837 /// * Non-Fungible Mode: Ignored838 /// * Fungible Mode: Must specify transferred amount839 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)840 #[weight = <CommonWeights<T>>::transfer()]841 #[transactional]842 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {843 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);844845 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))846 }847848 /// Set, change, or remove approved address to transfer the ownership of the NFT.849 ///850 /// # Permissions851 ///852 /// * Collection Owner853 /// * Collection Admin854 /// * Current NFT owner855 ///856 /// # Arguments857 ///858 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).859 ///860 /// * collection_id.861 ///862 /// * item_id: ID of the item.863 #[weight = <CommonWeights<T>>::approve()]864 #[transactional]865 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {866 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);867868 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))869 }870871 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.872 ///873 /// # Permissions874 /// * Collection Owner875 /// * Collection Admin876 /// * Current NFT owner877 /// * Address approved by current NFT owner878 ///879 /// # Arguments880 ///881 /// * from: Address that owns token.882 ///883 /// * recipient: Address of token recipient.884 ///885 /// * collection_id.886 ///887 /// * item_id: ID of the item.888 ///889 /// * value: Amount to transfer.890 #[weight = <CommonWeights<T>>::transfer_from()]891 #[transactional]892 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {893 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);894895 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))896 }897898 /// Set off-chain data schema.899 ///900 /// # Permissions901 ///902 /// * Collection Owner903 /// * Collection Admin904 ///905 /// # Arguments906 ///907 /// * collection_id.908 ///909 /// * schema: String representing the offchain data schema.910 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]911 #[transactional]912 pub fn set_variable_meta_data (913 origin,914 collection_id: CollectionId,915 item_id: TokenId,916 data: BoundedVec<u8, CustomDataLimit>,917 ) -> DispatchResultWithPostInfo {918 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);919920 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))921 }922923 /// Set meta_update_permission value for particular collection924 ///925 /// # Permissions926 ///927 /// * Collection Owner.928 ///929 /// # Arguments930 ///931 /// * collection_id: ID of the collection.932 ///933 /// * value: New flag value.934 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]935 #[transactional]936 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {937 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);938 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;939940 ensure!(941 target_collection.meta_update_permission != MetaUpdatePermission::None,942 <CommonError<T>>::MetadataFlagFrozen,943 );944 target_collection.check_is_owner(&sender)?;945946 target_collection.meta_update_permission = value;947948 target_collection.save()949 }950951 /// Set schema standard952 /// ImageURL953 /// Unique954 ///955 /// # Permissions956 ///957 /// * Collection Owner958 /// * Collection Admin959 ///960 /// # Arguments961 ///962 /// * collection_id.963 ///964 /// * schema: SchemaVersion: enum965 #[weight = <SelfWeightOf<T>>::set_schema_version()]966 #[transactional]967 pub fn set_schema_version(968 origin,969 collection_id: CollectionId,970 version: SchemaVersion971 ) -> DispatchResult {972 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);973 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;974 target_collection.check_is_owner_or_admin(&sender)?;975 target_collection.schema_version = version;976977 <Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(978 collection_id979 ));980981 target_collection.save()982 }983984 /// Set off-chain data schema.985 ///986 /// # Permissions987 ///988 /// * Collection Owner989 /// * Collection Admin990 ///991 /// # Arguments992 ///993 /// * collection_id.994 ///995 /// * schema: String representing the offchain data schema.996 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]997 #[transactional]998 pub fn set_offchain_schema(999 origin,1000 collection_id: CollectionId,1001 schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1002 ) -> DispatchResult {1003 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1004 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1005 target_collection.check_is_owner_or_admin(&sender)?;10061007 target_collection.offchain_schema = schema;10081009 <Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1010 collection_id1011 ));10121013 target_collection.save()1014 }10151016 /// Set const on-chain data schema.1017 ///1018 /// # Permissions1019 ///1020 /// * Collection Owner1021 /// * Collection Admin1022 ///1023 /// # Arguments1024 ///1025 /// * collection_id.1026 ///1027 /// * schema: String representing the const on-chain data schema.1028 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1029 #[transactional]1030 pub fn set_const_on_chain_schema (1031 origin,1032 collection_id: CollectionId,1033 schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1034 ) -> DispatchResult {1035 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1037 target_collection.check_is_owner_or_admin(&sender)?;10381039 target_collection.const_on_chain_schema = schema;10401041 <Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1042 collection_id1043 ));10441045 target_collection.save()1046 }10471048 /// Set variable on-chain data schema.1049 ///1050 /// # Permissions1051 ///1052 /// * Collection Owner1053 /// * Collection Admin1054 ///1055 /// # Arguments1056 ///1057 /// * collection_id.1058 ///1059 /// * schema: String representing the variable on-chain data schema.1060 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1061 #[transactional]1062 pub fn set_variable_on_chain_schema (1063 origin,1064 collection_id: CollectionId,1065 schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1066 ) -> DispatchResult {1067 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1068 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1069 target_collection.check_is_owner_or_admin(&sender)?;10701071 target_collection.variable_on_chain_schema = schema;10721073 <Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1074 collection_id1075 ));10761077 target_collection.save()1078 }10791080 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1081 #[transactional]1082 pub fn set_collection_limits(1083 origin,1084 collection_id: CollectionId,1085 new_limit: CollectionLimits,1086 ) -> DispatchResult {1087 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1088 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1089 target_collection.check_is_owner(&sender)?;1090 let old_limit = &target_collection.limits;10911092 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;10931094 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1095 collection_id1096 ));10971098 target_collection.save()1099 }1100 }1101}1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_module, decl_storage, decl_error, decl_event,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced, Randomness,24 IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32 pallet_prelude::{DispatchResultWithPostInfo, ConstU32},33 BoundedVec,34};35use scale_info::TypeInfo;36use frame_system::{self as system, ensure_signed};37use sp_runtime::{sp_std::prelude::Vec};38use up_data_structs::{39 MAX_DECIMAL_POINTS, VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT,40 OFFCHAIN_SCHEMA_LIMIT, MAX_COLLECTION_NAME_LENGTH, MAX_COLLECTION_DESCRIPTION_LENGTH,41 MAX_TOKEN_PREFIX_LENGTH, AccessMode, CreateItemData, CollectionLimits, CollectionId,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,43 CreateCollectionData, CustomDataLimit, CreateItemExData,44};45use pallet_common::{46 account::CrossAccountId, CollectionHandle, Pallet as PalletCommon, Error as CommonError,47 CommonWeightInfo,48};49use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};50use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};51use pallet_nonfungible::{Pallet as PalletNonfungible, NonfungibleHandle};5253#[cfg(test)]54mod mock;5556#[cfg(test)]57mod tests;5859mod eth;60mod sponsorship;61pub use sponsorship::UniqueSponsorshipHandler;62pub use eth::sponsoring::UniqueEthSponsorshipHandler;6364pub use eth::UniqueErcSupport;6566pub mod common;67use common::CommonWeights;68pub mod dispatch;69use dispatch::dispatch_call;7071#[cfg(feature = "runtime-benchmarks")]72mod benchmarking;73pub mod weights;74use weights::WeightInfo;7576decl_error! {77 /// Error for non-fungible-token module.78 pub enum Error for Module<T: Config> {79 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.80 CollectionDecimalPointLimitExceeded,81 /// This address is not set as sponsor, use setCollectionSponsor first.82 ConfirmUnsetSponsorFail,83 /// Length of items properties must be greater than 0.84 EmptyArgument,85 }86}8788pub trait Config:89 system::Config90 + pallet_evm_coder_substrate::Config91 + pallet_common::Config92 + pallet_nonfungible::Config93 + pallet_refungible::Config94 + pallet_fungible::Config95 + Sized96 + TypeInfo97{98 type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;99100 /// Weight information for extrinsics in this pallet.101 type WeightInfo: WeightInfo;102}103104decl_event! {105 pub enum Event<T>106 where107 <T as frame_system::Config>::AccountId,108 <T as pallet_common::Config>::CrossAccountId,109 {110 /// Collection sponsor was removed111 ///112 /// # Arguments113 ///114 /// * collection_id: Globally unique collection identifier.115 CollectionSponsorRemoved(CollectionId),116117 /// Collection admin was added118 ///119 /// # Arguments120 ///121 /// * collection_id: Globally unique collection identifier.122 ///123 /// * admin: Admin address.124 CollectionAdminAdded(CollectionId, CrossAccountId),125126 /// Collection owned was change127 ///128 /// # Arguments129 ///130 /// * collection_id: Globally unique collection identifier.131 ///132 /// * owner: New owner address.133 CollectionOwnedChanged(CollectionId, AccountId),134135 /// Collection sponsor was set136 ///137 /// # Arguments138 ///139 /// * collection_id: Globally unique collection identifier.140 ///141 /// * owner: New sponsor address.142 CollectionSponsorSet(CollectionId, AccountId),143144 /// const on chain schema was set145 ///146 /// # Arguments147 ///148 /// * collection_id: Globally unique collection identifier.149 ConstOnChainSchemaSet(CollectionId),150151 /// New sponsor was confirm152 ///153 /// # Arguments154 ///155 /// * collection_id: Globally unique collection identifier.156 ///157 /// * sponsor: New sponsor address.158 SponsorshipConfirmed(CollectionId, AccountId),159160 /// Collection admin was removed161 ///162 /// # Arguments163 ///164 /// * collection_id: Globally unique collection identifier.165 ///166 /// * admin: Admin address.167 CollectionAdminRemoved(CollectionId, CrossAccountId),168169 /// Address was remove from allow list170 ///171 /// # Arguments172 ///173 /// * collection_id: Globally unique collection identifier.174 ///175 /// * user: Address.176 AllowListAddressRemoved(CollectionId, CrossAccountId),177178 /// Address was add to allow list179 ///180 /// # Arguments181 ///182 /// * collection_id: Globally unique collection identifier.183 ///184 /// * user: Address.185 AllowListAddressAdded(CollectionId, CrossAccountId),186187 /// Collection limits was set188 ///189 /// # Arguments190 ///191 /// * collection_id: Globally unique collection identifier.192 CollectionLimitSet(CollectionId),193194 /// Mint permission was set195 ///196 /// # Arguments197 ///198 /// * collection_id: Globally unique collection identifier.199 MintPermissionSet(CollectionId),200201 /// Offchain schema was set202 ///203 /// # Arguments204 ///205 /// * collection_id: Globally unique collection identifier.206 OffchainSchemaSet(CollectionId),207208 /// Public access mode was set209 ///210 /// # Arguments211 ///212 /// * collection_id: Globally unique collection identifier.213 ///214 /// * mode: New access state.215 PublicAccessModeSet(CollectionId, AccessMode),216217 /// Schema version was set218 ///219 /// # Arguments220 ///221 /// * collection_id: Globally unique collection identifier.222 SchemaVersionSet(CollectionId),223224 /// Variable on chain schema was set225 ///226 /// # Arguments227 ///228 /// * collection_id: Globally unique collection identifier.229 VariableOnChainSchemaSet(CollectionId),230 }231}232233type SelfWeightOf<T> = <T as Config>::WeightInfo;234235// # Used definitions236//237// ## User control levels238//239// chain-controlled - key is uncontrolled by user240// i.e autoincrementing index241// can use non-cryptographic hash242// real - key is controlled by user243// but it is hard to generate enough colliding values, i.e owner of signed txs244// can use non-cryptographic hash245// controlled - key is completly controlled by users246// i.e maps with mutable keys247// should use cryptographic hash248//249// ## User control level downgrade reasons250//251// ?1 - chain-controlled -> controlled252// collections/tokens can be destroyed, resulting in massive holes253// ?2 - chain-controlled -> controlled254// same as ?1, but can be only added, resulting in easier exploitation255// ?3 - real -> controlled256// no confirmation required, so addresses can be easily generated257decl_storage! {258 trait Store for Module<T: Config> as Unique {259260 //#region Private members261 /// Used for migrations262 ChainVersion: u64;263 //#endregion264265 //#region Tokens transfer rate limit baskets266 /// (Collection id (controlled?2), who created (real))267 /// TODO: Off chain worker should remove from this map when collection gets removed268 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => Option<T::BlockNumber>;269 /// Collection id (controlled?2), token id (controlled?2)270 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;271 /// Collection id (controlled?2), owning user (real)272 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;273 /// Collection id (controlled?2), token id (controlled?2)274 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;275 //#endregion276277 /// Variable metadata sponsoring278 /// Collection id (controlled?2), token id (controlled?2)279 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;280 /// Approval sponsoring281 pub NftApproveBasket get(fn nft_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber>;282 pub FungibleApproveBasket get(fn fungible_approve_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;283 pub RefungibleApproveBasket get(fn refungible_approve_basket): nmap hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId, hasher(twox_64_concat) T::AccountId => Option<T::BlockNumber>;284 }285}286287decl_module! {288 pub struct Module<T: Config> for enum Call289 where290 origin: T::Origin291 {292 type Error = Error<T>;293294 fn deposit_event() = default;295296 fn on_initialize(_now: T::BlockNumber) -> Weight {297 0298 }299300 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner of the collection is set to the address that signed the transaction and can be changed later.301 ///302 /// # Permissions303 ///304 /// * Anyone.305 ///306 /// # Arguments307 ///308 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.309 ///310 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.311 ///312 /// * token_prefix: UTF-8 string with token prefix.313 ///314 /// * mode: [CollectionMode] collection type and type dependent data.315 // returns collection ID316 #[weight = <SelfWeightOf<T>>::create_collection()]317 #[transactional]318 #[deprecated]319 pub fn create_collection(origin,320 collection_name: BoundedVec<u16, ConstU32<MAX_COLLECTION_NAME_LENGTH>>,321 collection_description: BoundedVec<u16, ConstU32<MAX_COLLECTION_DESCRIPTION_LENGTH>>,322 token_prefix: BoundedVec<u8, ConstU32<MAX_TOKEN_PREFIX_LENGTH>>,323 mode: CollectionMode) -> DispatchResult {324 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {325 name: collection_name,326 description: collection_description,327 token_prefix,328 mode,329 ..Default::default()330 };331 Self::create_collection_ex(origin, data)332 }333334 /// This method creates a collection335 ///336 /// Prefer it to deprecated [`created_collection`] method337 #[weight = <SelfWeightOf<T>>::create_collection()]338 #[transactional]339 pub fn create_collection_ex(origin, data: CreateCollectionData<T::AccountId>) -> DispatchResult {340 let owner = ensure_signed(origin)?;341342 let _id = match data.mode {343 CollectionMode::NFT => {<PalletNonfungible<T>>::init_collection(owner, data)?},344 CollectionMode::Fungible(decimal_points) => {345 // check params346 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);347 <PalletFungible<T>>::init_collection(owner, data)?348 }349 CollectionMode::ReFungible => {350 <PalletRefungible<T>>::init_collection(owner, data)?351 }352 };353354 Ok(())355 }356357 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.358 ///359 /// # Permissions360 ///361 /// * Collection Owner.362 ///363 /// # Arguments364 ///365 /// * collection_id: collection to destroy.366 #[weight = <SelfWeightOf<T>>::destroy_collection()]367 #[transactional]368 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {369 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);370371 let collection = <CollectionHandle<T>>::try_get(collection_id)?;372 collection.check_is_owner(&sender)?;373374 // =========375376 match collection.mode {377 CollectionMode::ReFungible => PalletRefungible::destroy_collection(RefungibleHandle::cast(collection), &sender)?,378 CollectionMode::Fungible(_) => PalletFungible::destroy_collection(FungibleHandle::cast(collection), &sender)?,379 CollectionMode::NFT => PalletNonfungible::destroy_collection(NonfungibleHandle::cast(collection), &sender)?,380 }381382 <NftTransferBasket<T>>::remove_prefix(collection_id, None);383 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);384 <ReFungibleTransferBasket<T>>::remove_prefix((collection_id,), None);385386 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);387 <NftApproveBasket<T>>::remove_prefix(collection_id, None);388 <FungibleApproveBasket<T>>::remove_prefix(collection_id, None);389 <RefungibleApproveBasket<T>>::remove_prefix((collection_id,), None);390391 Ok(())392 }393394 /// Add an address to allow list.395 ///396 /// # Permissions397 ///398 /// * Collection Owner399 /// * Collection Admin400 ///401 /// # Arguments402 ///403 /// * collection_id.404 ///405 /// * address.406 #[weight = <SelfWeightOf<T>>::add_to_allow_list()]407 #[transactional]408 pub fn add_to_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{409410 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);411 let collection = <CollectionHandle<T>>::try_get(collection_id)?;412413 <PalletCommon<T>>::toggle_allowlist(414 &collection,415 &sender,416 &address,417 true,418 )?;419420 Self::deposit_event(Event::<T>::AllowListAddressAdded(421 collection_id,422 address423 ));424425 Ok(())426 }427428 /// Remove an address from allow list.429 ///430 /// # Permissions431 ///432 /// * Collection Owner433 /// * Collection Admin434 ///435 /// # Arguments436 ///437 /// * collection_id.438 ///439 /// * address.440 #[weight = <SelfWeightOf<T>>::remove_from_allow_list()]441 #[transactional]442 pub fn remove_from_allow_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{443444 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);445 let collection = <CollectionHandle<T>>::try_get(collection_id)?;446447 <PalletCommon<T>>::toggle_allowlist(448 &collection,449 &sender,450 &address,451 false,452 )?;453454 <Pallet<T>>::deposit_event(Event::<T>::AllowListAddressRemoved(455 collection_id,456 address457 ));458459 Ok(())460 }461462 /// Toggle between normal and allow list access for the methods with access for `Anyone`.463 ///464 /// # Permissions465 ///466 /// * Collection Owner.467 ///468 /// # Arguments469 ///470 /// * collection_id.471 ///472 /// * mode: [AccessMode]473 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]474 #[transactional]475 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult476 {477 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);478479 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;480 target_collection.check_is_owner(&sender)?;481482 target_collection.access = mode.clone();483484 <Pallet<T>>::deposit_event(Event::<T>::PublicAccessModeSet(485 collection_id,486 mode487 ));488489 target_collection.save()490 }491492 /// Allows Anyone to create tokens if:493 /// * Allow List is enabled, and494 /// * Address is added to allow list, and495 /// * This method was called with True parameter496 ///497 /// # Permissions498 /// * Collection Owner499 ///500 /// # Arguments501 ///502 /// * collection_id.503 ///504 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.505 #[weight = <SelfWeightOf<T>>::set_mint_permission()]506 #[transactional]507 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult508 {509 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);510511 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;512 target_collection.check_is_owner(&sender)?;513514 target_collection.mint_mode = mint_permission;515516 <Pallet<T>>::deposit_event(Event::<T>::MintPermissionSet(517 collection_id518 ));519520 target_collection.save()521 }522523 /// Change the owner of the collection.524 ///525 /// # Permissions526 ///527 /// * Collection Owner.528 ///529 /// # Arguments530 ///531 /// * collection_id.532 ///533 /// * new_owner.534 #[weight = <SelfWeightOf<T>>::change_collection_owner()]535 #[transactional]536 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {537538 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);539540 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;541 target_collection.check_is_owner(&sender)?;542543 target_collection.owner = new_owner.clone();544 <Pallet<T>>::deposit_event(Event::<T>::CollectionOwnedChanged(545 collection_id,546 new_owner547 ));548549 target_collection.save()550 }551552 /// Adds an admin of the Collection.553 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.554 ///555 /// # Permissions556 ///557 /// * Collection Owner.558 /// * Collection Admin.559 ///560 /// # Arguments561 ///562 /// * collection_id: ID of the Collection to add admin for.563 ///564 /// * new_admin_id: Address of new admin to add.565 #[weight = <SelfWeightOf<T>>::add_collection_admin()]566 #[transactional]567 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {568 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);569 let collection = <CollectionHandle<T>>::try_get(collection_id)?;570571 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminAdded(572 collection_id,573 new_admin_id.clone()574 ));575576 <PalletCommon<T>>::toggle_admin(&collection, &sender, &new_admin_id, true)577 }578579 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.580 ///581 /// # Permissions582 ///583 /// * Collection Owner.584 /// * Collection Admin.585 ///586 /// # Arguments587 ///588 /// * collection_id: ID of the Collection to remove admin for.589 ///590 /// * account_id: Address of admin to remove.591 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]592 #[transactional]593 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {594 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);595 let collection = <CollectionHandle<T>>::try_get(collection_id)?;596597 <Pallet<T>>::deposit_event(Event::<T>::CollectionAdminRemoved(598 collection_id,599 account_id.clone()600 ));601602 <PalletCommon<T>>::toggle_admin(&collection, &sender, &account_id, false)603 }604605 /// # Permissions606 ///607 /// * Collection Owner608 ///609 /// # Arguments610 ///611 /// * collection_id.612 ///613 /// * new_sponsor.614 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]615 #[transactional]616 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {617 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);618619 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;620 target_collection.check_is_owner(&sender)?;621622 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor.clone());623624 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorSet(625 collection_id,626 new_sponsor627 ));628629 target_collection.save()630 }631632 /// # Permissions633 ///634 /// * Sponsor.635 ///636 /// # Arguments637 ///638 /// * collection_id.639 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]640 #[transactional]641 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {642 let sender = ensure_signed(origin)?;643644 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;645 ensure!(646 target_collection.sponsorship.pending_sponsor() == Some(&sender),647 Error::<T>::ConfirmUnsetSponsorFail648 );649650 target_collection.sponsorship = SponsorshipState::Confirmed(sender.clone());651652 <Pallet<T>>::deposit_event(Event::<T>::SponsorshipConfirmed(653 collection_id,654 sender655 ));656657 target_collection.save()658 }659660 /// Switch back to pay-per-own-transaction model.661 ///662 /// # Permissions663 ///664 /// * Collection owner.665 ///666 /// # Arguments667 ///668 /// * collection_id.669 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]670 #[transactional]671 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {672 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);673674 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;675 target_collection.check_is_owner(&sender)?;676677 target_collection.sponsorship = SponsorshipState::Disabled;678679 <Pallet<T>>::deposit_event(Event::<T>::CollectionSponsorRemoved(680 collection_id681 ));682 target_collection.save()683 }684685 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.686 ///687 /// # Permissions688 ///689 /// * Collection Owner.690 /// * Collection Admin.691 /// * Anyone if692 /// * Allow List is enabled, and693 /// * Address is added to allow list, and694 /// * MintPermission is enabled (see SetMintPermission method)695 ///696 /// # Arguments697 ///698 /// * collection_id: ID of the collection.699 ///700 /// * owner: Address, initial owner of the NFT.701 ///702 /// * data: Token data to store on chain.703 #[weight = <CommonWeights<T>>::create_item()]704 #[transactional]705 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResultWithPostInfo {706 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);707708 dispatch_call::<T, _>(collection_id, |d| d.create_item(sender, owner, data))709 }710711 /// This method creates multiple items in a collection created with CreateCollection method.712 ///713 /// # Permissions714 ///715 /// * Collection Owner.716 /// * Collection Admin.717 /// * Anyone if718 /// * Allow List is enabled, and719 /// * Address is added to allow list, and720 /// * MintPermission is enabled (see SetMintPermission method)721 ///722 /// # Arguments723 ///724 /// * collection_id: ID of the collection.725 ///726 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].727 ///728 /// * owner: Address, initial owner of the NFT.729 #[weight = <CommonWeights<T>>::create_multiple_items(items_data.len() as u32)]730 #[transactional]731 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResultWithPostInfo {732 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);733 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);734735 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items(sender, owner, items_data))736 }737738 #[weight = <CommonWeights<T>>::create_multiple_items_ex(&data)]739 #[transactional]740 pub fn create_multiple_items_ex(origin, collection_id: CollectionId, data: CreateItemExData<T::CrossAccountId>) -> DispatchResultWithPostInfo {741 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);742743 dispatch_call::<T, _>(collection_id, |d| d.create_multiple_items_ex(sender, data))744 }745746 // TODO! transaction weight747748 /// Set transfers_enabled value for particular collection749 ///750 /// # Permissions751 ///752 /// * Collection Owner.753 ///754 /// # Arguments755 ///756 /// * collection_id: ID of the collection.757 ///758 /// * value: New flag value.759 #[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]760 #[transactional]761 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {762 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;764 target_collection.check_is_owner(&sender)?;765766 // =========767768 target_collection.limits.transfers_enabled = Some(value);769 target_collection.save()770 }771772 /// Destroys a concrete instance of NFT.773 ///774 /// # Permissions775 ///776 /// * Collection Owner.777 /// * Collection Admin.778 /// * Current NFT Owner.779 ///780 /// # Arguments781 ///782 /// * collection_id: ID of the collection.783 ///784 /// * item_id: ID of NFT to burn.785 #[weight = <CommonWeights<T>>::burn_item()]786 #[transactional]787 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {788 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);789790 let post_info = dispatch_call::<T, _>(collection_id, |d| d.burn_item(sender, item_id, value))?;791 if value == 1 {792 <NftTransferBasket<T>>::remove(collection_id, item_id);793 <NftApproveBasket<T>>::remove(collection_id, item_id);794 }795 // Those maps should be cleared only if token disappears completly, need to move this part of logic to pallets?796 // <FungibleApproveBasket<T>>::remove(collection_id, sender.as_sub());797 // <RefungibleApproveBasket<T>>::remove((collection_id, item_id, sender.as_sub()));798 Ok(post_info)799 }800801 /// Destroys a concrete instance of NFT on behalf of the owner802 /// See also: [`approve`]803 ///804 /// # Permissions805 ///806 /// * Collection Owner.807 /// * Collection Admin.808 /// * Current NFT Owner.809 ///810 /// # Arguments811 ///812 /// * collection_id: ID of the collection.813 ///814 /// * item_id: ID of NFT to burn.815 ///816 /// * from: owner of item817 #[weight = <CommonWeights<T>>::burn_from()]818 #[transactional]819 pub fn burn_from(origin, collection_id: CollectionId, from: T::CrossAccountId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {820 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);821822 dispatch_call::<T, _>(collection_id, |d| d.burn_from(sender, from, item_id, value))823 }824825 /// Change ownership of the token.826 ///827 /// # Permissions828 ///829 /// * Collection Owner830 /// * Collection Admin831 /// * Current NFT owner832 ///833 /// # Arguments834 ///835 /// * recipient: Address of token recipient.836 ///837 /// * collection_id.838 ///839 /// * item_id: ID of the item840 /// * Non-Fungible Mode: Required.841 /// * Fungible Mode: Ignored.842 /// * Re-Fungible Mode: Required.843 ///844 /// * value: Amount to transfer.845 /// * Non-Fungible Mode: Ignored846 /// * Fungible Mode: Must specify transferred amount847 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)848 #[weight = <CommonWeights<T>>::transfer()]849 #[transactional]850 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResultWithPostInfo {851 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);852853 dispatch_call::<T, _>(collection_id, |d| d.transfer(sender, recipient, item_id, value))854 }855856 /// Set, change, or remove approved address to transfer the ownership of the NFT.857 ///858 /// # Permissions859 ///860 /// * Collection Owner861 /// * Collection Admin862 /// * Current NFT owner863 ///864 /// # Arguments865 ///866 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).867 ///868 /// * collection_id.869 ///870 /// * item_id: ID of the item.871 #[weight = <CommonWeights<T>>::approve()]872 #[transactional]873 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResultWithPostInfo {874 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);875876 dispatch_call::<T, _>(collection_id, |d| d.approve(sender, spender, item_id, amount))877 }878879 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.880 ///881 /// # Permissions882 /// * Collection Owner883 /// * Collection Admin884 /// * Current NFT owner885 /// * Address approved by current NFT owner886 ///887 /// # Arguments888 ///889 /// * from: Address that owns token.890 ///891 /// * recipient: Address of token recipient.892 ///893 /// * collection_id.894 ///895 /// * item_id: ID of the item.896 ///897 /// * value: Amount to transfer.898 #[weight = <CommonWeights<T>>::transfer_from()]899 #[transactional]900 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResultWithPostInfo {901 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902903 dispatch_call::<T, _>(collection_id, |d| d.transfer_from(sender, from, recipient, item_id, value))904 }905906 /// Set off-chain data schema.907 ///908 /// # Permissions909 ///910 /// * Collection Owner911 /// * Collection Admin912 ///913 /// # Arguments914 ///915 /// * collection_id.916 ///917 /// * schema: String representing the offchain data schema.918 #[weight = <CommonWeights<T>>::set_variable_metadata(data.len() as u32)]919 #[transactional]920 pub fn set_variable_meta_data (921 origin,922 collection_id: CollectionId,923 item_id: TokenId,924 data: BoundedVec<u8, CustomDataLimit>,925 ) -> DispatchResultWithPostInfo {926 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);927928 dispatch_call::<T, _>(collection_id, |d| d.set_variable_metadata(sender, item_id, data))929 }930931 /// Set meta_update_permission value for particular collection932 ///933 /// # Permissions934 ///935 /// * Collection Owner.936 ///937 /// # Arguments938 ///939 /// * collection_id: ID of the collection.940 ///941 /// * value: New flag value.942 #[weight = <SelfWeightOf<T>>::set_meta_update_permission_flag()]943 #[transactional]944 pub fn set_meta_update_permission_flag(origin, collection_id: CollectionId, value: MetaUpdatePermission) -> DispatchResult {945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;947948 ensure!(949 target_collection.meta_update_permission != MetaUpdatePermission::None,950 <CommonError<T>>::MetadataFlagFrozen,951 );952 target_collection.check_is_owner(&sender)?;953954 target_collection.meta_update_permission = value;955956 target_collection.save()957 }958959 /// Set schema standard960 /// ImageURL961 /// Unique962 ///963 /// # Permissions964 ///965 /// * Collection Owner966 /// * Collection Admin967 ///968 /// # Arguments969 ///970 /// * collection_id.971 ///972 /// * schema: SchemaVersion: enum973 #[weight = <SelfWeightOf<T>>::set_schema_version()]974 #[transactional]975 pub fn set_schema_version(976 origin,977 collection_id: CollectionId,978 version: SchemaVersion979 ) -> DispatchResult {980 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;982 target_collection.check_is_owner_or_admin(&sender)?;983 target_collection.schema_version = version;984985 <Pallet<T>>::deposit_event(Event::<T>::SchemaVersionSet(986 collection_id987 ));988989 target_collection.save()990 }991992 /// Set off-chain data schema.993 ///994 /// # Permissions995 ///996 /// * Collection Owner997 /// * Collection Admin998 ///999 /// # Arguments1000 ///1001 /// * collection_id.1002 ///1003 /// * schema: String representing the offchain data schema.1004 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1005 #[transactional]1006 pub fn set_offchain_schema(1007 origin,1008 collection_id: CollectionId,1009 schema: BoundedVec<u8, ConstU32<OFFCHAIN_SCHEMA_LIMIT>>,1010 ) -> DispatchResult {1011 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1012 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1013 target_collection.check_is_owner_or_admin(&sender)?;10141015 target_collection.offchain_schema = schema;10161017 <Pallet<T>>::deposit_event(Event::<T>::OffchainSchemaSet(1018 collection_id1019 ));10201021 target_collection.save()1022 }10231024 /// Set const on-chain data schema.1025 ///1026 /// # Permissions1027 ///1028 /// * Collection Owner1029 /// * Collection Admin1030 ///1031 /// # Arguments1032 ///1033 /// * collection_id.1034 ///1035 /// * schema: String representing the const on-chain data schema.1036 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1037 #[transactional]1038 pub fn set_const_on_chain_schema (1039 origin,1040 collection_id: CollectionId,1041 schema: BoundedVec<u8, ConstU32<CONST_ON_CHAIN_SCHEMA_LIMIT>>1042 ) -> DispatchResult {1043 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1044 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1045 target_collection.check_is_owner_or_admin(&sender)?;10461047 target_collection.const_on_chain_schema = schema;10481049 <Pallet<T>>::deposit_event(Event::<T>::ConstOnChainSchemaSet(1050 collection_id1051 ));10521053 target_collection.save()1054 }10551056 /// Set variable on-chain data schema.1057 ///1058 /// # Permissions1059 ///1060 /// * Collection Owner1061 /// * Collection Admin1062 ///1063 /// # Arguments1064 ///1065 /// * collection_id.1066 ///1067 /// * schema: String representing the variable on-chain data schema.1068 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1069 #[transactional]1070 pub fn set_variable_on_chain_schema (1071 origin,1072 collection_id: CollectionId,1073 schema: BoundedVec<u8, ConstU32<VARIABLE_ON_CHAIN_SCHEMA_LIMIT>>1074 ) -> DispatchResult {1075 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1076 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1077 target_collection.check_is_owner_or_admin(&sender)?;10781079 target_collection.variable_on_chain_schema = schema;10801081 <Pallet<T>>::deposit_event(Event::<T>::VariableOnChainSchemaSet(1082 collection_id1083 ));10841085 target_collection.save()1086 }10871088 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1089 #[transactional]1090 pub fn set_collection_limits(1091 origin,1092 collection_id: CollectionId,1093 new_limit: CollectionLimits,1094 ) -> DispatchResult {1095 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1096 let mut target_collection = <CollectionHandle<T>>::try_get(collection_id)?;1097 target_collection.check_is_owner(&sender)?;1098 let old_limit = &target_collection.limits;10991100 target_collection.limits = <PalletCommon<T>>::clamp_limits(target_collection.mode.clone(), &old_limit, new_limit)?;11011102 <Pallet<T>>::deposit_event(Event::<T>::CollectionLimitSet(1103 collection_id1104 ));11051106 target_collection.save()1107 }1108 }1109}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 {