difftreelog
feat nft benchmarking
in: master
8 files changed
Makefilediffbeforeafterboth--- a/Makefile
+++ b/Makefile
@@ -10,5 +10,9 @@
bench-evm-migration:
make _bench PALLET=evm-migration
+.PHONY: bench-nft
+bench-nft:
+ make _bench PALLET=nft
+
.PHONY: bench
-bench: bench-evm-migration
+bench: bench-evm-migration bench-nft
pallets/nft/src/benchmarking.rsdiffbeforeafterboth--- a/pallets/nft/src/benchmarking.rs
+++ b/pallets/nft/src/benchmarking.rs
@@ -1,419 +1,309 @@
#![cfg(feature = "runtime-benchmarks")]
use super::*;
-use crate::Module as Nft;
-
-use sp_std::prelude::*;
+use crate::Pallet;
use frame_system::RawOrigin;
-use frame_benchmarking::{benchmarks, account, whitelisted_caller}; // , TrackedStorageKey,
+use frame_benchmarking::{benchmarks, account};
+use nft_data_structs::*;
+use core::convert::TryInto;
+use sp_runtime::DispatchError;
const SEED: u32 = 1;
-/*
+
+fn create_data(size: usize) -> Vec<u8> {
+ (0..size).map(|v| (v & 0xff) as u8).collect()
+}
+fn create_u16_data(size: usize) -> Vec<u16> {
+ (0..size).map(|v| (v & 0xffff) as u16).collect()
+}
+
fn default_nft_data() -> CreateItemData {
- CreateItemData::NFT(CreateNftData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+ CreateItemData::NFT(CreateNftData {
+ const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+ variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+ })
+}
+
+fn default_fungible_data() -> CreateItemData {
+ CreateItemData::Fungible(CreateFungibleData { value: 1000 })
}
-fn default_fungible_data () -> CreateItemData {
- CreateItemData::Fungible(CreateFungibleData { })
+fn default_re_fungible_data() -> CreateItemData {
+ CreateItemData::ReFungible(CreateReFungibleData {
+ const_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+ variable_data: create_data(CUSTOM_DATA_LIMIT as usize).try_into().unwrap(),
+ pieces: 1000,
+ })
}
-fn default_re_fungible_data () -> CreateItemData {
- CreateItemData::ReFungible(CreateReFungibleData { const_data: vec![1, 2, 3], variable_data: vec![3, 2, 1] })
+fn create_collection_helper<T: Config>(
+ owner: T::AccountId,
+ mode: CollectionMode,
+) -> Result<CollectionId, DispatchError> {
+ T::Currency::deposit_creating(&owner, T::CollectionCreationPrice::get());
+ let col_name = create_u16_data(MAX_COLLECTION_NAME_LENGTH)
+ .try_into()
+ .unwrap();
+ let col_desc = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH)
+ .try_into()
+ .unwrap();
+ let token_prefix = create_data(MAX_TOKEN_PREFIX_LENGTH).try_into().unwrap();
+ <Pallet<T>>::create_collection(
+ RawOrigin::Signed(owner).into(),
+ col_name,
+ col_desc,
+ token_prefix,
+ mode,
+ )?;
+ Ok(CreatedCollectionCount::get())
+}
+fn create_nft_collection<T: Config>(owner: T::AccountId) -> Result<CollectionId, DispatchError> {
+ create_collection_helper::<T>(owner, CollectionMode::NFT)
+}
+fn create_fungible_collection<T: Config>(
+ owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+ create_collection_helper::<T>(owner, CollectionMode::Fungible(0))
+}
+fn create_refungible_collection<T: Config>(
+ owner: T::AccountId,
+) -> Result<CollectionId, DispatchError> {
+ create_collection_helper::<T>(owner, CollectionMode::ReFungible)
}
-*/
benchmarks! {
create_collection {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
+ let col_name: Vec<u16> = create_u16_data(MAX_COLLECTION_NAME_LENGTH);
+ let col_desc: Vec<u16> = create_u16_data(MAX_COLLECTION_DESCRIPTION_LENGTH);
+ let token_prefix: Vec<u8> = create_data(MAX_TOKEN_PREFIX_LENGTH);
let mode: CollectionMode = CollectionMode::NFT;
let caller: T::AccountId = account("caller", 0, SEED);
- }: _(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)
-/*
+ T::Currency::deposit_creating(&caller, T::CollectionCreationPrice::get());
+ }: _(RawOrigin::Signed(caller.clone()), col_name.clone(), col_desc.clone(), token_prefix.clone(), mode)
verify {
- assert_eq!(Nft::<T>::collection_id(2).owner, caller);
+ assert_eq!(<Pallet<T>>::collection_id(2).unwrap().owner, caller);
}
+
destroy_collection {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: _(RawOrigin::Signed(caller.clone()), 2)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection)
add_to_white_list {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ let caller: T::AccountId = account("caller", 0, SEED);
let whitelist_account: T::AccountId = account("admin", 0, SEED);
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: add_to_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
remove_from_white_list {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ let caller: T::AccountId = account("caller", 0, SEED);
let whitelist_account: T::AccountId = account("admin", 0, SEED);
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), 2, whitelist_account.clone())?;
- }: remove_from_white_list(RawOrigin::Signed(caller.clone()), 2, whitelist_account)
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ <Pallet<T>>::add_to_white_list(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(whitelist_account.clone()))?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(whitelist_account))
set_public_access_mode {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: set_public_access_mode(RawOrigin::Signed(caller.clone()), 2, AccessMode::WhiteList)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, AccessMode::WhiteList)
set_mint_permission {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: set_mint_permission(RawOrigin::Signed(caller.clone()), 2, true)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, true)
change_collection_owner {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let new_owner: T::AccountId = account("admin", 0, SEED);
- }: change_collection_owner(RawOrigin::Signed(caller.clone()), 2, new_owner)
+ }: _(RawOrigin::Signed(caller.clone()), collection, new_owner)
add_collection_admin {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let new_admin: T::AccountId = account("admin", 0, SEED);
- }: add_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+ }: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
remove_collection_admin {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let new_admin: T::AccountId = account("admin", 0, SEED);
- Nft::<T>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, new_admin.clone())?;
- }: remove_collection_admin(RawOrigin::Signed(caller.clone()), 2, new_admin)
+ <Pallet<T>>::add_collection_admin(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(new_admin.clone()))?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(new_admin))
set_collection_sponsor {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: set_collection_sponsor(RawOrigin::Signed(caller.clone()), 2, caller.clone())
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection, caller.clone())
confirm_sponsorship {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
- }: confirm_sponsorship(RawOrigin::Signed(caller.clone()), 2)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ <Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+ }: _(RawOrigin::Signed(caller.clone()), collection)
remove_collection_sponsor {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- Nft::<T>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
- Nft::<T>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
- }: remove_collection_sponsor(RawOrigin::Signed(caller.clone()), 2)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ <Pallet<T>>::set_collection_sponsor(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone())?;
+ <Pallet<T>>::confirm_sponsorship(RawOrigin::Signed(caller.clone()).into(), 2)?;
+ }: _(RawOrigin::Signed(caller.clone()), collection)
// nft item
create_item_nft {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let data = default_nft_data();
-
- }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
-
- #[extra]
- create_item_nft_large {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- let mut nft_data = CreateNftData {
- const_data: vec![],
- variable_data: vec![]
- };
- for i in 0..1998 {
- nft_data.const_data.push(10);
- nft_data.variable_data.push(10);
- }
- let data = CreateItemData::NFT(nft_data);
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let b in 0..(CUSTOM_DATA_LIMIT * 2);
- }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ let data = CreateItemData::NFT(CreateNftData {
+ const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+ variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+ });
+ }: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
// fungible item
create_item_fungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let data = default_fungible_data();
-
- }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_fungible_collection::<T>(caller.clone())?;
+ let data = CreateItemData::Fungible(CreateFungibleData {
+ value: 1000,
+ });
+ }: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
// refungible item
create_item_refungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let data = default_re_fungible_data();
+ let b in 0..(CUSTOM_DATA_LIMIT * 2);
- }: create_item(RawOrigin::Signed(caller.clone()), 2, caller.clone(), data)
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_refungible_collection::<T>(caller.clone())?;
+ let data = CreateItemData::ReFungible(CreateReFungibleData {
+ const_data: create_data(b.min(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+ variable_data: create_data(b.saturating_sub(CUSTOM_DATA_LIMIT) as usize).try_into().unwrap(),
+ pieces: 1000,
+ });
+ }: create_item(RawOrigin::Signed(caller.clone()), collection, T::CrossAccountId::from_sub(caller.clone()), data)
- burn_item {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ burn_item_nft {
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let data = default_nft_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
- }: burn_item(RawOrigin::Signed(caller.clone()), 2, 1)
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ }: burn_item(RawOrigin::Signed(caller.clone()), collection, 1, 1)
transfer_nft {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_nft_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
- }: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ }: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
transfer_fungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_fungible_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_fungible_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ }: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
- }: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
-
transfer_refungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_refungible_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_re_fungible_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), collection, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ }: transfer(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
- }: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1, 1)
-
- approve {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
+ approve_nft {
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let data = default_re_fungible_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
-
- }: approve(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 1)
+ let data = default_nft_data();
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ }: approve(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), collection, 1, 1)
// Nft
transfer_from_nft {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_nft_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
- Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
- }: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ <Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+ }: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
// Fungible
transfer_from_fungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::Fungible(3);
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_fungible_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_fungible_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
- Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ <Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+ }: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
- }: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
-
// ReFungible
transfer_from_refungible {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_refungible_collection::<T>(caller.clone())?;
let recipient: T::AccountId = account("recipient", 0, SEED);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
let data = default_re_fungible_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
- Nft::<T>::approve(RawOrigin::Signed(caller.clone()).into(), recipient.clone(), 2, 1)?;
-
- }: transfer_from(RawOrigin::Signed(caller.clone()), caller.clone(), recipient.clone(), 2, 1, 1)
-
- enable_contract_sponsoring {
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ <Pallet<T>>::approve(RawOrigin::Signed(caller.clone()).into(), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)?;
+ }: transfer_from(RawOrigin::Signed(caller.clone()), T::CrossAccountId::from_sub(caller.clone()), T::CrossAccountId::from_sub(recipient.clone()), 2, 1, 1)
- }: enable_contract_sponsoring(RawOrigin::Signed(caller.clone()), caller.clone(), true)
-
set_offchain_schema {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let b in 0..OFFCHAIN_SCHEMA_LIMIT;
- }: set_offchain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ let data = create_data(b as usize);
+ }: set_offchain_schema(RawOrigin::Signed(caller.clone()), collection, data)
set_const_on_chain_schema {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+ let b in 0..CONST_ON_CHAIN_SCHEMA_LIMIT;
+
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ let data = create_data(b as usize);
+ }: set_const_on_chain_schema(RawOrigin::Signed(caller.clone()), collection, data)
set_variable_on_chain_schema {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::ReFungible(3);
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- }: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+ let b in 0..VARIABLE_ON_CHAIN_SCHEMA_LIMIT;
- set_variable_meta_data {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let data = default_nft_data();
- Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, caller.clone(), data)?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ let data = create_data(b as usize);
+ }: set_variable_on_chain_schema(RawOrigin::Signed(caller.clone()), 2, data)
- }: set_variable_meta_data(RawOrigin::Signed(caller.clone()), 2, 1, [1, 2, 3].to_vec())
+ set_variable_meta_data_nft {
+ let b in 0..CUSTOM_DATA_LIMIT;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
+ let data = default_nft_data();
+ <Pallet<T>>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, T::CrossAccountId::from_sub(caller.clone()), data)?;
+ let data = create_data(b as usize);
+ }: set_variable_meta_data(RawOrigin::Signed(caller.clone()), collection, 1, data)
+
set_schema_version {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
}: set_schema_version(RawOrigin::Signed(caller.clone()), 2, SchemaVersion::Unique)
-
- set_chain_limits {
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- let limits = ChainLimits {
- collection_numbers_limit: 0,
- account_token_ownership_limit: 0,
- collections_admins_limit: 0,
- custom_data_limit: 0,
- nft_sponsor_transfer_timeout: 0,
- fungible_sponsor_transfer_timeout: 0,
- refungible_sponsor_transfer_timeout: 0
- };
- }: set_chain_limits(RawOrigin::Signed(caller.clone()), limits)
- set_contract_sponsoring_rate_limit {
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
- let block_number: T::BlockNumber = 0.into();
- }: set_contract_sponsoring_rate_limit(RawOrigin::Signed(caller.clone()), caller.clone(), block_number)
-
set_collection_limits{
- let col_name1: Vec<u16> = "Test1".encode_utf16().collect::<Vec<u16>>();
- let col_desc1: Vec<u16> = "TestDescription1".encode_utf16().collect::<Vec<u16>>();
- let token_prefix1: Vec<u8> = b"token_prefix1".to_vec();
- let mode: CollectionMode = CollectionMode::NFT;
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::create_collection(RawOrigin::Signed(caller.clone()).into(), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode.clone())?;
+ let caller: T::AccountId = account("caller", 0, SEED);
+ let collection = create_nft_collection::<T>(caller.clone())?;
let cl = CollectionLimits {
account_token_ownership_limit: 0,
sponsored_data_size: 0,
- token_limit: 0,
- sponsor_transfer_timeout: 0
+ token_limit: 1,
+ sponsor_transfer_timeout: 0,
+ owner_can_destroy: true,
+ owner_can_transfer: true,
+ sponsored_data_rate_limit: None,
};
-
}: set_collection_limits(RawOrigin::Signed(caller.clone()), 2, cl)
-
- add_to_contract_white_list{
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- }: add_to_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
-
- remove_from_contract_white_list{
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- Nft::<T>::add_to_contract_white_list(RawOrigin::Signed(caller.clone()).into(), caller.clone(), caller.clone())?;
- }: remove_from_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), caller.clone())
-
- toggle_contract_white_list{
- let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
- }: toggle_contract_white_list(RawOrigin::Signed(caller.clone()), caller.clone(), true)
-*/
}
pallets/nft/src/default_weights.rsdiffbeforeafterboth--- a/pallets/nft/src/default_weights.rs
+++ /dev/null
@@ -1,150 +0,0 @@
-use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
-
-impl crate::WeightInfo for () {
- fn create_collection() -> Weight {
- 70_000_000_u64
- .saturating_add(DbWeight::get().reads(7_u64))
- .saturating_add(DbWeight::get().writes(5_u64))
- }
- fn destroy_collection() -> Weight {
- 90_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(5_u64))
- }
- fn add_to_white_list() -> Weight {
- 30_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_from_white_list() -> Weight {
- 35_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_public_access_mode() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_mint_permission() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn change_collection_owner() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn add_collection_admin() -> Weight {
- 32_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_collection_admin() -> Weight {
- 50_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_collection_sponsor() -> Weight {
- 32_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn confirm_sponsorship() -> Weight {
- 22_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_collection_sponsor() -> Weight {
- 24_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn create_item(s: usize) -> Weight {
- 130_000_000_u64
- .saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temporary multiplier, fee for storage
- .saturating_add(DbWeight::get().reads(10_u64))
- .saturating_add(DbWeight::get().writes(8_u64))
- }
- fn burn_item() -> Weight {
- 170_000_000_u64
- .saturating_add(DbWeight::get().reads(9_u64))
- .saturating_add(DbWeight::get().writes(7_u64))
- }
- fn transfer() -> Weight {
- 125_000_000_u64
- .saturating_add(DbWeight::get().reads(7_u64))
- .saturating_add(DbWeight::get().writes(7_u64))
- }
- fn approve() -> Weight {
- 45_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn transfer_from() -> Weight {
- 150_000_000_u64
- .saturating_add(DbWeight::get().reads(9_u64))
- .saturating_add(DbWeight::get().writes(8_u64))
- }
- fn set_offchain_schema() -> Weight {
- 33_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_const_on_chain_schema() -> Weight {
- 11_100_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_variable_on_chain_schema() -> Weight {
- 11_100_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_variable_meta_data() -> Weight {
- 17_500_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn enable_contract_sponsoring() -> Weight {
- 13_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_schema_version() -> Weight {
- 8_500_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_contract_sponsoring_rate_limit() -> Weight {
- 3_500_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
- 3_500_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn toggle_contract_white_list() -> Weight {
- 3_000_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn add_to_contract_white_list() -> Weight {
- 3_000_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn remove_from_contract_white_list() -> Weight {
- 3_200_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn set_collection_limits() -> Weight {
- 8_900_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
-}
pallets/nft/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_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, 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};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45 FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;6667pub trait WeightInfo {68 fn create_collection() -> Weight;69 fn destroy_collection() -> Weight;70 fn add_to_white_list() -> Weight;71 fn remove_from_white_list() -> Weight;72 fn set_public_access_mode() -> Weight;73 fn set_mint_permission() -> Weight;74 fn change_collection_owner() -> Weight;75 fn add_collection_admin() -> Weight;76 fn remove_collection_admin() -> Weight;77 fn set_collection_sponsor() -> Weight;78 fn confirm_sponsorship() -> Weight;79 fn remove_collection_sponsor() -> Weight;80 fn create_item(s: usize) -> Weight;81 fn burn_item() -> Weight;82 fn transfer() -> Weight;83 fn approve() -> Weight;84 fn transfer_from() -> Weight;85 fn set_offchain_schema() -> Weight;86 fn set_const_on_chain_schema() -> Weight;87 fn set_variable_on_chain_schema() -> Weight;88 fn set_variable_meta_data() -> Weight;89 fn enable_contract_sponsoring() -> Weight;90 fn set_schema_version() -> Weight;91 fn set_contract_sponsoring_rate_limit() -> Weight;92 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;93 fn toggle_contract_white_list() -> Weight;94 fn add_to_contract_white_list() -> Weight;95 fn remove_from_contract_white_list() -> Weight;96 fn set_collection_limits() -> Weight;97}9899decl_error! {100 /// Error for non-fungible-token module.101 pub enum Error for Module<T: Config> {102 /// Total collections bound exceeded.103 TotalCollectionsLimitExceeded,104 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.105 CollectionDecimalPointLimitExceeded,106 /// Collection name can not be longer than 63 char.107 CollectionNameLimitExceeded,108 /// Collection description can not be longer than 255 char.109 CollectionDescriptionLimitExceeded,110 /// Token prefix can not be longer than 15 char.111 CollectionTokenPrefixLimitExceeded,112 /// This collection does not exist.113 CollectionNotFound,114 /// Item not exists.115 TokenNotFound,116 /// Admin not found117 AdminNotFound,118 /// Arithmetic calculation overflow.119 NumOverflow,120 /// Account already has admin role.121 AlreadyAdmin,122 /// You do not own this collection.123 NoPermission,124 /// This address is not set as sponsor, use setCollectionSponsor first.125 ConfirmUnsetSponsorFail,126 /// Collection is not in mint mode.127 PublicMintingNotAllowed,128 /// Sender parameter and item owner must be equal.129 MustBeTokenOwner,130 /// Item balance not enough.131 TokenValueTooLow,132 /// Size of item is too large.133 NftSizeLimitExceeded,134 /// No approve found135 ApproveNotFound,136 /// Requested value more than approved.137 TokenValueNotEnough,138 /// Only approved addresses can call this method.139 ApproveRequired,140 /// Address is not in white list.141 AddresNotInWhiteList,142 /// Number of collection admins bound exceeded.143 CollectionAdminsLimitExceeded,144 /// Owned tokens by a single address bound exceeded.145 AddressOwnershipLimitExceeded,146 /// Length of items properties must be greater than 0.147 EmptyArgument,148 /// const_data exceeded data limit.149 TokenConstDataLimitExceeded,150 /// variable_data exceeded data limit.151 TokenVariableDataLimitExceeded,152 /// Not NFT item data used to mint in NFT collection.153 NotNftDataUsedToMintNftCollectionToken,154 /// Not Fungible item data used to mint in Fungible collection.155 NotFungibleDataUsedToMintFungibleCollectionToken,156 /// Not Re Fungible item data used to mint in Re Fungible collection.157 NotReFungibleDataUsedToMintReFungibleCollectionToken,158 /// Unexpected collection type.159 UnexpectedCollectionType,160 /// Can't store metadata in fungible tokens.161 CantStoreMetadataInFungibleTokens,162 /// Collection token limit exceeded163 CollectionTokenLimitExceeded,164 /// Account token limit exceeded per collection165 AccountTokenLimitExceeded,166 /// Collection limit bounds per collection exceeded167 CollectionLimitBoundsExceeded,168 /// Tried to enable permissions which are only permitted to be disabled169 OwnerPermissionsCantBeReverted,170 /// Schema data size limit bound exceeded171 SchemaDataLimitExceeded,172 /// Maximum refungibility exceeded173 WrongRefungiblePieces,174 /// createRefungible should be called with one owner175 BadCreateRefungibleCall,176 /// Gas limit exceeded177 OutOfGas,178 /// Collection settings not allowing items transferring179 TransferNotAllowed,180 }181}182183#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]184pub struct CollectionHandle<T: Config> {185 pub id: CollectionId,186 collection: Collection<T>,187 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,188}189impl<T: Config> CollectionHandle<T> {190 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {191 <CollectionById<T>>::get(id).map(|collection| Self {192 id,193 collection,194 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(195 eth::collection_id_to_address(id),196 gas_limit,197 ),198 })199 }200 pub fn get(id: CollectionId) -> Option<Self> {201 Self::get_with_gas_limit(id, u64::MAX)202 }203 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {204 self.recorder.log_sub(log)205 }206 fn consume_gas(&self, gas: u64) -> DispatchResult {207 self.recorder.consume_gas_sub(gas)208 }209 pub fn submit_logs(self) -> DispatchResult {210 self.recorder.submit_logs()211 }212 pub fn save(self) -> DispatchResult {213 self.recorder.submit_logs()?;214 <CollectionById<T>>::insert(self.id, self.collection);215 Ok(())216 }217}218impl<T: Config> Deref for CollectionHandle<T> {219 type Target = Collection<T>;220221 fn deref(&self) -> &Self::Target {222 &self.collection223 }224}225226impl<T: Config> DerefMut for CollectionHandle<T> {227 fn deref_mut(&mut self) -> &mut Self::Target {228 &mut self.collection229 }230}231232pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {233 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;234235 /// Weight information for extrinsics in this pallet.236 type WeightInfo: WeightInfo;237238 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;239 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;240241 type CrossAccountId: CrossAccountId<Self::AccountId>;242 type Currency: Currency<Self::AccountId>;243 type CollectionCreationPrice: Get<244 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,245 >;246 type TreasuryAccountId: Get<Self::AccountId>;247}248249// # Used definitions250//251// ## User control levels252//253// chain-controlled - key is uncontrolled by user254// i.e autoincrementing index255// can use non-cryptographic hash256// real - key is controlled by user257// but it is hard to generate enough colliding values, i.e owner of signed txs258// can use non-cryptographic hash259// controlled - key is completly controlled by users260// i.e maps with mutable keys261// should use cryptographic hash262//263// ## User control level downgrade reasons264//265// ?1 - chain-controlled -> controlled266// collections/tokens can be destroyed, resulting in massive holes267// ?2 - chain-controlled -> controlled268// same as ?1, but can be only added, resulting in easier exploitation269// ?3 - real -> controlled270// no confirmation required, so addresses can be easily generated271decl_storage! {272 trait Store for Module<T: Config> as Nft {273274 //#region Private members275 /// Id of next collection276 CreatedCollectionCount: u32;277 /// Used for migrations278 ChainVersion: u64;279 /// Id of last collection token280 /// Collection id (controlled?1)281 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;282 //#endregion283284 //#region Bound counters285 /// Amount of collections destroyed, used for total amount tracking with286 /// CreatedCollectionCount287 DestroyedCollectionCount: u32;288 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)289 /// Account id (real)290 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;291 //#endregion292293 //#region Basic collections294 /// Collection info295 /// Collection id (controlled?1)296 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;297 /// List of collection admins298 /// Collection id (controlled?2)299 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;300 /// Whitelisted collection users301 /// Collection id (controlled?2), user id (controlled?3)302 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;303 //#endregion304305 /// How many of collection items user have306 /// Collection id (controlled?2), account id (real)307 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;308309 /// Amount of items which spender can transfer out of owners account (via transferFrom)310 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))311 /// TODO: Off chain worker should remove from this map when token gets removed312 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;313314 //#region Item collections315 /// Collection id (controlled?2), token id (controlled?1)316 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;317 /// Collection id (controlled?2), owner (controlled?2)318 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;319 /// Collection id (controlled?2), token id (controlled?1)320 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;321 //#endregion322323 //#region Index list324 /// Collection id (controlled?2), tokens owner (controlled?2)325 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;326 //#endregion327328 //#region Tokens transfer rate limit baskets329 /// (Collection id (controlled?2), who created (real))330 /// TODO: Off chain worker should remove from this map when collection gets removed331 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;332 /// Collection id (controlled?2), token id (controlled?2)333 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;334 /// Collection id (controlled?2), owning user (real)335 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;336 /// Collection id (controlled?2), token id (controlled?2)337 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;338 //#endregion339340 /// Variable metadata sponsoring341 /// Collection id (controlled?2), token id (controlled?2)342 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;343 }344 add_extra_genesis {345 build(|config: &GenesisConfig<T>| {346 // Modification of storage347 for (_num, _c) in &config.collection_id {348 <Module<T>>::init_collection(_c);349 }350351 for (_num, _c, _i) in &config.nft_item_id {352 <Module<T>>::init_nft_token(*_c, _i);353 }354355 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {356 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);357 }358359 for (_num, _c, _i) in &config.refungible_item_id {360 <Module<T>>::init_refungible_token(*_c, _i);361 }362 })363 }364}365366decl_event!(367 pub enum Event<T>368 where369 AccountId = <T as frame_system::Config>::AccountId,370 CrossAccountId = <T as Config>::CrossAccountId,371 {372 /// New collection was created373 ///374 /// # Arguments375 ///376 /// * collection_id: Globally unique identifier of newly created collection.377 ///378 /// * mode: [CollectionMode] converted into u8.379 ///380 /// * account_id: Collection owner.381 CollectionCreated(CollectionId, u8, AccountId),382383 /// New item was created.384 ///385 /// # Arguments386 ///387 /// * collection_id: Id of the collection where item was created.388 ///389 /// * item_id: Id of an item. Unique within the collection.390 ///391 /// * recipient: Owner of newly created item392 ItemCreated(CollectionId, TokenId, CrossAccountId),393394 /// Collection item was burned.395 ///396 /// # Arguments397 ///398 /// collection_id.399 ///400 /// item_id: Identifier of burned NFT.401 ItemDestroyed(CollectionId, TokenId),402403 /// Item was transferred404 ///405 /// * collection_id: Id of collection to which item is belong406 ///407 /// * item_id: Id of an item408 ///409 /// * sender: Original owner of item410 ///411 /// * recipient: New owner of item412 ///413 /// * amount: Always 1 for NFT414 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),415416 /// * collection_id417 ///418 /// * item_id419 ///420 /// * sender421 ///422 /// * spender423 ///424 /// * amount425 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),426 }427);428429decl_module! {430 pub struct Module<T: Config> for enum Call431 where432 origin: T::Origin433 {434 fn deposit_event() = default;435 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;436 type Error = Error<T>;437438 fn on_initialize(_now: T::BlockNumber) -> Weight {439 0440 }441442 /// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.443 ///444 /// # Permissions445 ///446 /// * Anyone.447 ///448 /// # Arguments449 ///450 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.451 ///452 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.453 ///454 /// * token_prefix: UTF-8 string with token prefix.455 ///456 /// * mode: [CollectionMode] collection type and type dependent data.457 // returns collection ID458 #[weight = <T as Config>::WeightInfo::create_collection()]459 #[transactional]460 pub fn create_collection(origin,461 collection_name: Vec<u16>,462 collection_description: Vec<u16>,463 token_prefix: Vec<u8>,464 mode: CollectionMode) -> DispatchResult {465466 // Anyone can create a collection467 let who = ensure_signed(origin)?;468469 // Take a (non-refundable) deposit of collection creation470 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();471 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(472 &T::TreasuryAccountId::get(),473 T::CollectionCreationPrice::get(),474 ));475 <T as Config>::Currency::settle(476 &who,477 imbalance,478 WithdrawReasons::TRANSFER,479 ExistenceRequirement::KeepAlive,480 ).map_err(|_| Error::<T>::NoPermission)?;481482 let decimal_points = match mode {483 CollectionMode::Fungible(points) => points,484 _ => 0485 };486487 let created_count = CreatedCollectionCount::get();488 let destroyed_count = DestroyedCollectionCount::get();489490 // bound Total number of collections491 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);492493 // check params494 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);495 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);496 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);497 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);498499 // Generate next collection ID500 let next_id = created_count501 .checked_add(1)502 .ok_or(Error::<T>::NumOverflow)?;503504 CreatedCollectionCount::put(next_id);505506 let limits = CollectionLimits {507 sponsored_data_size: CUSTOM_DATA_LIMIT,508 ..Default::default()509 };510511 // Create new collection512 let new_collection = Collection {513 owner: who.clone(),514 name: collection_name,515 mode: mode.clone(),516 mint_mode: false,517 access: AccessMode::Normal,518 description: collection_description,519 decimal_points,520 token_prefix,521 offchain_schema: Vec::new(),522 schema_version: SchemaVersion::ImageURL,523 sponsorship: SponsorshipState::Disabled,524 variable_on_chain_schema: Vec::new(),525 const_on_chain_schema: Vec::new(),526 limits,527 transfers_enabled: true,528 };529530 // Add new collection to map531 <CollectionById<T>>::insert(next_id, new_collection);532533 // call event534 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));535536 Ok(())537 }538539 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.540 ///541 /// # Permissions542 ///543 /// * Collection Owner.544 ///545 /// # Arguments546 ///547 /// * collection_id: collection to destroy.548 #[weight = <T as Config>::WeightInfo::destroy_collection()]549 #[transactional]550 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {551552 let sender = ensure_signed(origin)?;553 let collection = Self::get_collection(collection_id)?;554 Self::check_owner_permissions(&collection, &sender)?;555 if !collection.limits.owner_can_destroy {556 fail!(Error::<T>::NoPermission);557 }558559 <AddressTokens<T>>::remove_prefix(collection_id, None);560 <Allowances<T>>::remove_prefix(collection_id, None);561 <Balance<T>>::remove_prefix(collection_id, None);562 <ItemListIndex>::remove(collection_id);563 <AdminList<T>>::remove(collection_id);564 <CollectionById<T>>::remove(collection_id);565 <WhiteList<T>>::remove_prefix(collection_id, None);566567 <NftItemList<T>>::remove_prefix(collection_id, None);568 <FungibleItemList<T>>::remove_prefix(collection_id, None);569 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);570571 <NftTransferBasket<T>>::remove_prefix(collection_id, None);572 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);573 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);574575 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);576577 DestroyedCollectionCount::put(DestroyedCollectionCount::get()578 .checked_add(1)579 .ok_or(Error::<T>::NumOverflow)?);580581 Ok(())582 }583584 /// Add an address to white list.585 ///586 /// # Permissions587 ///588 /// * Collection Owner589 /// * Collection Admin590 ///591 /// # Arguments592 ///593 /// * collection_id.594 ///595 /// * address.596 #[weight = <T as Config>::WeightInfo::add_to_white_list()]597 #[transactional]598 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{599600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let collection = Self::get_collection(collection_id)?;602603 Self::toggle_white_list_internal(604 &sender,605 &collection,606 &address,607 true,608 )?;609610 Ok(())611 }612613 /// Remove an address from white list.614 ///615 /// # Permissions616 ///617 /// * Collection Owner618 /// * Collection Admin619 ///620 /// # Arguments621 ///622 /// * collection_id.623 ///624 /// * address.625 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]626 #[transactional]627 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{628629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630 let collection = Self::get_collection(collection_id)?;631632 Self::toggle_white_list_internal(633 &sender,634 &collection,635 &address,636 false,637 )?;638639 Ok(())640 }641642 /// Toggle between normal and white list access for the methods with access for `Anyone`.643 ///644 /// # Permissions645 ///646 /// * Collection Owner.647 ///648 /// # Arguments649 ///650 /// * collection_id.651 ///652 /// * mode: [AccessMode]653 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]654 #[transactional]655 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult656 {657 let sender = ensure_signed(origin)?;658659 let mut target_collection = Self::get_collection(collection_id)?;660 Self::check_owner_permissions(&target_collection, &sender)?;661 target_collection.access = mode;662 target_collection.save()663 }664665 /// Allows Anyone to create tokens if:666 /// * White List is enabled, and667 /// * Address is added to white list, and668 /// * This method was called with True parameter669 ///670 /// # Permissions671 /// * Collection Owner672 ///673 /// # Arguments674 ///675 /// * collection_id.676 ///677 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.678 #[weight = <T as Config>::WeightInfo::set_mint_permission()]679 #[transactional]680 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult681 {682 let sender = ensure_signed(origin)?;683684 let mut target_collection = Self::get_collection(collection_id)?;685 Self::check_owner_permissions(&target_collection, &sender)?;686 target_collection.mint_mode = mint_permission;687 target_collection.save()688 }689690 /// Change the owner of the collection.691 ///692 /// # Permissions693 ///694 /// * Collection Owner.695 ///696 /// # Arguments697 ///698 /// * collection_id.699 ///700 /// * new_owner.701 #[weight = <T as Config>::WeightInfo::change_collection_owner()]702 #[transactional]703 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {704705 let sender = ensure_signed(origin)?;706 let mut target_collection = Self::get_collection(collection_id)?;707 Self::check_owner_permissions(&target_collection, &sender)?;708 target_collection.owner = new_owner;709 target_collection.save()710 }711712 /// Adds an admin of the Collection.713 /// 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.714 ///715 /// # Permissions716 ///717 /// * Collection Owner.718 /// * Collection Admin.719 ///720 /// # Arguments721 ///722 /// * collection_id: ID of the Collection to add admin for.723 ///724 /// * new_admin_id: Address of new admin to add.725 #[weight = <T as Config>::WeightInfo::add_collection_admin()]726 #[transactional]727 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {728 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729 let collection = Self::get_collection(collection_id)?;730 Self::check_owner_or_admin_permissions(&collection, &sender)?;731 let mut admin_arr = <AdminList<T>>::get(collection_id);732733 match admin_arr.binary_search(&new_admin_id) {734 Ok(_) => {},735 Err(idx) => {736 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);737 admin_arr.insert(idx, new_admin_id);738 <AdminList<T>>::insert(collection_id, admin_arr);739 }740 }741 Ok(())742 }743744 /// 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.745 ///746 /// # Permissions747 ///748 /// * Collection Owner.749 /// * Collection Admin.750 ///751 /// # Arguments752 ///753 /// * collection_id: ID of the Collection to remove admin for.754 ///755 /// * account_id: Address of admin to remove.756 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]757 #[transactional]758 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {759 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);760 let collection = Self::get_collection(collection_id)?;761 Self::check_owner_or_admin_permissions(&collection, &sender)?;762 let mut admin_arr = <AdminList<T>>::get(collection_id);763764 if let Ok(idx) = admin_arr.binary_search(&account_id) {765 admin_arr.remove(idx);766 <AdminList<T>>::insert(collection_id, admin_arr);767 }768 Ok(())769 }770771 /// # Permissions772 ///773 /// * Collection Owner774 ///775 /// # Arguments776 ///777 /// * collection_id.778 ///779 /// * new_sponsor.780 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]781 #[transactional]782 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {783 let sender = ensure_signed(origin)?;784 let mut target_collection = Self::get_collection(collection_id)?;785 Self::check_owner_permissions(&target_collection, &sender)?;786787 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);788 target_collection.save()789 }790791 /// # Permissions792 ///793 /// * Sponsor.794 ///795 /// # Arguments796 ///797 /// * collection_id.798 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]799 #[transactional]800 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {801 let sender = ensure_signed(origin)?;802803 let mut target_collection = Self::get_collection(collection_id)?;804 ensure!(805 target_collection.sponsorship.pending_sponsor() == Some(&sender),806 Error::<T>::ConfirmUnsetSponsorFail807 );808809 target_collection.sponsorship = SponsorshipState::Confirmed(sender);810 target_collection.save()811 }812813 /// Switch back to pay-per-own-transaction model.814 ///815 /// # Permissions816 ///817 /// * Collection owner.818 ///819 /// # Arguments820 ///821 /// * collection_id.822 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]823 #[transactional]824 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {825 let sender = ensure_signed(origin)?;826827 let mut target_collection = Self::get_collection(collection_id)?;828 Self::check_owner_permissions(&target_collection, &sender)?;829830 target_collection.sponsorship = SponsorshipState::Disabled;831 target_collection.save()832 }833834 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.835 ///836 /// # Permissions837 ///838 /// * Collection Owner.839 /// * Collection Admin.840 /// * Anyone if841 /// * White List is enabled, and842 /// * Address is added to white list, and843 /// * MintPermission is enabled (see SetMintPermission method)844 ///845 /// # Arguments846 ///847 /// * collection_id: ID of the collection.848 ///849 /// * owner: Address, initial owner of the NFT.850 ///851 /// * data: Token data to store on chain.852 // #[weight =853 // (130_000_000 as Weight)854 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))855 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))856 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]857858 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]859 #[transactional]860 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {861 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);862 let collection = Self::get_collection(collection_id)?;863864 Self::create_item_internal(&sender, &collection, &owner, data)?;865866 collection.submit_logs()867 }868869 /// This method creates multiple items in a collection created with CreateCollection method.870 ///871 /// # Permissions872 ///873 /// * Collection Owner.874 /// * Collection Admin.875 /// * Anyone if876 /// * White List is enabled, and877 /// * Address is added to white list, and878 /// * MintPermission is enabled (see SetMintPermission method)879 ///880 /// # Arguments881 ///882 /// * collection_id: ID of the collection.883 ///884 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].885 ///886 /// * owner: Address, initial owner of the NFT.887 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()888 .map(|data| { data.data_size() })889 .sum())]890 #[transactional]891 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {892893 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);894 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895 let collection = Self::get_collection(collection_id)?;896897 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;898899 collection.submit_logs()900 }901902 // TODO! transaction weight903904 /// Set transfers_enabled value for particular collection905 ///906 /// # Permissions907 ///908 /// * Collection Owner.909 ///910 /// # Arguments911 ///912 /// * collection_id: ID of the collection.913 ///914 /// * value: New flag value.915 #[weight = <T as Config>::WeightInfo::burn_item()]916 #[transactional]917 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {918919 let sender = ensure_signed(origin)?;920 let mut target_collection = Self::get_collection(collection_id)?;921922 Self::check_owner_permissions(&target_collection, &sender)?;923924 target_collection.transfers_enabled = value;925 target_collection.save()926 }927928 /// Destroys a concrete instance of NFT.929 ///930 /// # Permissions931 ///932 /// * Collection Owner.933 /// * Collection Admin.934 /// * Current NFT Owner.935 ///936 /// # Arguments937 ///938 /// * collection_id: ID of the collection.939 ///940 /// * item_id: ID of NFT to burn.941 #[weight = <T as Config>::WeightInfo::burn_item()]942 #[transactional]943 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {944945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let target_collection = Self::get_collection(collection_id)?;947948 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;949950 target_collection.submit_logs()951 }952953 /// Change ownership of the token.954 ///955 /// # Permissions956 ///957 /// * Collection Owner958 /// * Collection Admin959 /// * Current NFT owner960 ///961 /// # Arguments962 ///963 /// * recipient: Address of token recipient.964 ///965 /// * collection_id.966 ///967 /// * item_id: ID of the item968 /// * Non-Fungible Mode: Required.969 /// * Fungible Mode: Ignored.970 /// * Re-Fungible Mode: Required.971 ///972 /// * value: Amount to transfer.973 /// * Non-Fungible Mode: Ignored974 /// * Fungible Mode: Must specify transferred amount975 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)976 #[weight = <T as Config>::WeightInfo::transfer()]977 #[transactional]978 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {979 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);980 let collection = Self::get_collection(collection_id)?;981982 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;983984 collection.submit_logs()985 }986987 /// Set, change, or remove approved address to transfer the ownership of the NFT.988 ///989 /// # Permissions990 ///991 /// * Collection Owner992 /// * Collection Admin993 /// * Current NFT owner994 ///995 /// # Arguments996 ///997 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).998 ///999 /// * collection_id.1000 ///1001 /// * item_id: ID of the item.1002 #[weight = <T as Config>::WeightInfo::approve()]1003 #[transactional]1004 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1005 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006 let collection = Self::get_collection(collection_id)?;10071008 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10091010 collection.submit_logs()1011 }10121013 /// 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.1014 ///1015 /// # Permissions1016 /// * Collection Owner1017 /// * Collection Admin1018 /// * Current NFT owner1019 /// * Address approved by current NFT owner1020 ///1021 /// # Arguments1022 ///1023 /// * from: Address that owns token.1024 ///1025 /// * recipient: Address of token recipient.1026 ///1027 /// * collection_id.1028 ///1029 /// * item_id: ID of the item.1030 ///1031 /// * value: Amount to transfer.1032 #[weight = <T as Config>::WeightInfo::transfer_from()]1033 #[transactional]1034 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1035 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036 let collection = Self::get_collection(collection_id)?;10371038 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10391040 collection.submit_logs()1041 }1042 // #[weight = 0]1043 // // let no_perm_mes = "You do not have permissions to modify this collection";1044 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1045 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1046 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10471048 // // // on_nft_received call10491050 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10511052 // Ok(())1053 // }10541055 /// Set off-chain data schema.1056 ///1057 /// # Permissions1058 ///1059 /// * Collection Owner1060 /// * Collection Admin1061 ///1062 /// # Arguments1063 ///1064 /// * collection_id.1065 ///1066 /// * schema: String representing the offchain data schema.1067 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1068 #[transactional]1069 pub fn set_variable_meta_data (1070 origin,1071 collection_id: CollectionId,1072 item_id: TokenId,1073 data: Vec<u8>1074 ) -> DispatchResult {1075 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10761077 let collection = Self::get_collection(collection_id)?;10781079 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10801081 Ok(())1082 }10831084 /// Set schema standard1085 /// ImageURL1086 /// Unique1087 ///1088 /// # Permissions1089 ///1090 /// * Collection Owner1091 /// * Collection Admin1092 ///1093 /// # Arguments1094 ///1095 /// * collection_id.1096 ///1097 /// * schema: SchemaVersion: enum1098 #[weight = <T as Config>::WeightInfo::set_schema_version()]1099 #[transactional]1100 pub fn set_schema_version(1101 origin,1102 collection_id: CollectionId,1103 version: SchemaVersion1104 ) -> DispatchResult {1105 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1106 let mut target_collection = Self::get_collection(collection_id)?;1107 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1108 target_collection.schema_version = version;1109 target_collection.save()1110 }11111112 /// Set off-chain data schema.1113 ///1114 /// # Permissions1115 ///1116 /// * Collection Owner1117 /// * Collection Admin1118 ///1119 /// # Arguments1120 ///1121 /// * collection_id.1122 ///1123 /// * schema: String representing the offchain data schema.1124 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1125 #[transactional]1126 pub fn set_offchain_schema(1127 origin,1128 collection_id: CollectionId,1129 schema: Vec<u8>1130 ) -> DispatchResult {1131 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1132 let mut target_collection = Self::get_collection(collection_id)?;1133 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11341135 // check schema limit1136 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11371138 target_collection.offchain_schema = schema;1139 target_collection.save()1140 }11411142 /// Set const on-chain data schema.1143 ///1144 /// # Permissions1145 ///1146 /// * Collection Owner1147 /// * Collection Admin1148 ///1149 /// # Arguments1150 ///1151 /// * collection_id.1152 ///1153 /// * schema: String representing the const on-chain data schema.1154 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1155 #[transactional]1156 pub fn set_const_on_chain_schema (1157 origin,1158 collection_id: CollectionId,1159 schema: Vec<u8>1160 ) -> DispatchResult {1161 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1162 let mut target_collection = Self::get_collection(collection_id)?;1163 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11641165 // check schema limit1166 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11671168 target_collection.const_on_chain_schema = schema;1169 target_collection.save()1170 }11711172 /// Set variable on-chain data schema.1173 ///1174 /// # Permissions1175 ///1176 /// * Collection Owner1177 /// * Collection Admin1178 ///1179 /// # Arguments1180 ///1181 /// * collection_id.1182 ///1183 /// * schema: String representing the variable on-chain data schema.1184 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1185 #[transactional]1186 pub fn set_variable_on_chain_schema (1187 origin,1188 collection_id: CollectionId,1189 schema: Vec<u8>1190 ) -> DispatchResult {1191 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1192 let mut target_collection = Self::get_collection(collection_id)?;1193 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11941195 // check schema limit1196 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11971198 target_collection.variable_on_chain_schema = schema;1199 target_collection.save()1200 }12011202 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1203 #[transactional]1204 pub fn set_collection_limits(1205 origin,1206 collection_id: u32,1207 new_limits: CollectionLimits<T::BlockNumber>,1208 ) -> DispatchResult {1209 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210 let mut target_collection = Self::get_collection(collection_id)?;1211 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1212 let old_limits = &target_collection.limits;12131214 // collection bounds1215 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1216 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1217 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1218 Error::<T>::CollectionLimitBoundsExceeded);12191220 // token_limit check prev1221 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1222 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12231224 ensure!(1225 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1226 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1227 Error::<T>::OwnerPermissionsCantBeReverted,1228 );12291230 target_collection.limits = new_limits;12311232 target_collection.save()1233 }1234 }1235}12361237impl<T: Config> Module<T> {1238 pub fn create_item_internal(1239 sender: &T::CrossAccountId,1240 collection: &CollectionHandle<T>,1241 owner: &T::CrossAccountId,1242 data: CreateItemData,1243 ) -> DispatchResult {1244 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1245 Self::validate_create_item_args(collection, &data)?;1246 Self::create_item_no_validation(collection, owner, data)?;12471248 Ok(())1249 }12501251 pub fn transfer_internal(1252 sender: &T::CrossAccountId,1253 recipient: &T::CrossAccountId,1254 target_collection: &CollectionHandle<T>,1255 item_id: TokenId,1256 value: u128,1257 ) -> DispatchResult {1258 target_collection.consume_gas(2000000)?;1259 // Limits check1260 Self::is_correct_transfer(target_collection, recipient)?;12611262 // Transfer permissions check1263 ensure!(1264 Self::is_item_owner(sender, target_collection, item_id)1265 || Self::is_owner_or_admin_permissions(target_collection, sender),1266 Error::<T>::NoPermission1267 );12681269 if target_collection.access == AccessMode::WhiteList {1270 Self::check_white_list(target_collection, sender)?;1271 Self::check_white_list(target_collection, recipient)?;1272 }12731274 match target_collection.mode {1275 CollectionMode::NFT => Self::transfer_nft(1276 target_collection,1277 item_id,1278 sender.clone(),1279 recipient.clone(),1280 )?,1281 CollectionMode::Fungible(_) => {1282 Self::transfer_fungible(target_collection, value, sender, recipient)?1283 }1284 CollectionMode::ReFungible => Self::transfer_refungible(1285 target_collection,1286 item_id,1287 value,1288 sender.clone(),1289 recipient.clone(),1290 )?,1291 _ => (),1292 };12931294 Self::deposit_event(RawEvent::Transfer(1295 target_collection.id,1296 item_id,1297 sender.clone(),1298 recipient.clone(),1299 value,1300 ));13011302 Ok(())1303 }13041305 pub fn approve_internal(1306 sender: &T::CrossAccountId,1307 spender: &T::CrossAccountId,1308 collection: &CollectionHandle<T>,1309 item_id: TokenId,1310 amount: u128,1311 ) -> DispatchResult {1312 collection.consume_gas(2000000)?;1313 Self::token_exists(collection, item_id)?;13141315 // Transfer permissions check1316 let bypasses_limits = collection.limits.owner_can_transfer1317 && Self::is_owner_or_admin_permissions(collection, sender);13181319 let allowance_limit = if bypasses_limits {1320 None1321 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1322 Some(amount)1323 } else {1324 fail!(Error::<T>::NoPermission);1325 };13261327 if collection.access == AccessMode::WhiteList {1328 Self::check_white_list(collection, sender)?;1329 Self::check_white_list(collection, spender)?;1330 }13311332 let allowance: u128 = amount1333 .checked_add(<Allowances<T>>::get(1334 collection.id,1335 (item_id, sender.as_sub(), spender.as_sub()),1336 ))1337 .ok_or(Error::<T>::NumOverflow)?;1338 if let Some(limit) = allowance_limit {1339 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1340 }1341 <Allowances<T>>::insert(1342 collection.id,1343 (item_id, sender.as_sub(), spender.as_sub()),1344 allowance,1345 );13461347 if matches!(collection.mode, CollectionMode::NFT) {1348 // TODO: NFT: only one owner may exist for token in ERC7211349 collection.log(ERC721Events::Approval {1350 owner: *sender.as_eth(),1351 approved: *spender.as_eth(),1352 token_id: item_id.into(),1353 })?;1354 }13551356 if matches!(collection.mode, CollectionMode::Fungible(_)) {1357 // TODO: NFT: only one owner may exist for token in ERC201358 collection.log(ERC20Events::Approval {1359 owner: *sender.as_eth(),1360 spender: *spender.as_eth(),1361 value: allowance.into(),1362 })?;1363 }13641365 Self::deposit_event(RawEvent::Approved(1366 collection.id,1367 item_id,1368 sender.clone(),1369 spender.clone(),1370 allowance,1371 ));1372 Ok(())1373 }13741375 pub fn transfer_from_internal(1376 sender: &T::CrossAccountId,1377 from: &T::CrossAccountId,1378 recipient: &T::CrossAccountId,1379 collection: &CollectionHandle<T>,1380 item_id: TokenId,1381 amount: u128,1382 ) -> DispatchResult {1383 collection.consume_gas(2000000)?;1384 // Check approval1385 let approval: u128 =1386 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13871388 // Limits check1389 Self::is_correct_transfer(collection, recipient)?;13901391 // Transfer permissions check1392 ensure!(1393 approval >= amount1394 || (collection.limits.owner_can_transfer1395 && Self::is_owner_or_admin_permissions(collection, sender)),1396 Error::<T>::NoPermission1397 );13981399 if collection.access == AccessMode::WhiteList {1400 Self::check_white_list(collection, sender)?;1401 Self::check_white_list(collection, recipient)?;1402 }14031404 // Reduce approval by transferred amount or remove if remaining approval drops to 01405 let allowance = approval.saturating_sub(amount);1406 if allowance > 0 {1407 <Allowances<T>>::insert(1408 collection.id,1409 (item_id, from.as_sub(), sender.as_sub()),1410 allowance,1411 );1412 } else {1413 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1414 }14151416 match collection.mode {1417 CollectionMode::NFT => {1418 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1419 }1420 CollectionMode::Fungible(_) => {1421 Self::transfer_fungible(collection, amount, from, recipient)?1422 }1423 CollectionMode::ReFungible => Self::transfer_refungible(1424 collection,1425 item_id,1426 amount,1427 from.clone(),1428 recipient.clone(),1429 )?,1430 _ => (),1431 };14321433 if matches!(collection.mode, CollectionMode::Fungible(_)) {1434 collection.log(ERC20Events::Approval {1435 owner: *from.as_eth(),1436 spender: *sender.as_eth(),1437 value: allowance.into(),1438 })?;1439 }14401441 Ok(())1442 }14431444 pub fn set_variable_meta_data_internal(1445 sender: &T::CrossAccountId,1446 collection: &CollectionHandle<T>,1447 item_id: TokenId,1448 data: Vec<u8>,1449 ) -> DispatchResult {1450 Self::token_exists(collection, item_id)?;14511452 ensure!(1453 CUSTOM_DATA_LIMIT >= data.len() as u32,1454 Error::<T>::TokenVariableDataLimitExceeded1455 );14561457 // Modify permissions check1458 ensure!(1459 Self::is_item_owner(sender, collection, item_id)1460 || Self::is_owner_or_admin_permissions(collection, sender),1461 Error::<T>::NoPermission1462 );14631464 match collection.mode {1465 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1466 CollectionMode::ReFungible => {1467 Self::set_re_fungible_variable_data(collection, item_id, data)?1468 }1469 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1470 _ => fail!(Error::<T>::UnexpectedCollectionType),1471 };14721473 Ok(())1474 }14751476 pub fn create_multiple_items_internal(1477 sender: &T::CrossAccountId,1478 collection: &CollectionHandle<T>,1479 owner: &T::CrossAccountId,1480 items_data: Vec<CreateItemData>,1481 ) -> DispatchResult {1482 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14831484 for data in &items_data {1485 Self::validate_create_item_args(collection, data)?;1486 }1487 for data in &items_data {1488 Self::create_item_no_validation(collection, owner, data.clone())?;1489 }14901491 Ok(())1492 }14931494 pub fn burn_item_internal(1495 sender: &T::CrossAccountId,1496 collection: &CollectionHandle<T>,1497 item_id: TokenId,1498 value: u128,1499 ) -> DispatchResult {1500 ensure!(1501 Self::is_item_owner(sender, collection, item_id)1502 || (collection.limits.owner_can_transfer1503 && Self::is_owner_or_admin_permissions(collection, sender)),1504 Error::<T>::NoPermission1505 );15061507 if collection.access == AccessMode::WhiteList {1508 Self::check_white_list(collection, sender)?;1509 }15101511 match collection.mode {1512 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1513 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1514 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1515 _ => (),1516 };15171518 Ok(())1519 }15201521 pub fn toggle_white_list_internal(1522 sender: &T::CrossAccountId,1523 collection: &CollectionHandle<T>,1524 address: &T::CrossAccountId,1525 whitelisted: bool,1526 ) -> DispatchResult {1527 Self::check_owner_or_admin_permissions(collection, sender)?;15281529 if whitelisted {1530 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1531 } else {1532 <WhiteList<T>>::remove(collection.id, address.as_sub());1533 }15341535 Ok(())1536 }15371538 fn is_correct_transfer(1539 collection: &CollectionHandle<T>,1540 recipient: &T::CrossAccountId,1541 ) -> DispatchResult {1542 let collection_id = collection.id;15431544 // check token limit and account token limit1545 let account_items: u32 =1546 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1547 ensure!(1548 collection.limits.account_token_ownership_limit > account_items,1549 Error::<T>::AccountTokenLimitExceeded1550 );15511552 // preliminary transfer check1553 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15541555 Ok(())1556 }15571558 fn can_create_items_in_collection(1559 collection: &CollectionHandle<T>,1560 sender: &T::CrossAccountId,1561 owner: &T::CrossAccountId,1562 amount: u32,1563 ) -> DispatchResult {1564 let collection_id = collection.id;15651566 // check token limit and account token limit1567 let total_items: u32 = ItemListIndex::get(collection_id)1568 .checked_add(amount)1569 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1570 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1571 as u32)1572 .checked_add(amount)1573 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1574 ensure!(1575 collection.limits.token_limit >= total_items,1576 Error::<T>::CollectionTokenLimitExceeded1577 );1578 ensure!(1579 collection.limits.account_token_ownership_limit >= account_items,1580 Error::<T>::AccountTokenLimitExceeded1581 );15821583 if !Self::is_owner_or_admin_permissions(collection, sender) {1584 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1585 Self::check_white_list(collection, owner)?;1586 Self::check_white_list(collection, sender)?;1587 }15881589 Ok(())1590 }15911592 fn validate_create_item_args(1593 target_collection: &CollectionHandle<T>,1594 data: &CreateItemData,1595 ) -> DispatchResult {1596 match target_collection.mode {1597 CollectionMode::NFT => {1598 if !matches!(data, CreateItemData::NFT(_)) {1599 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1600 }1601 }1602 CollectionMode::Fungible(_) => {1603 if !matches!(data, CreateItemData::Fungible(_)) {1604 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1605 }1606 }1607 CollectionMode::ReFungible => {1608 if let CreateItemData::ReFungible(data) = data {1609 // Check refungibility limits1610 ensure!(1611 data.pieces <= MAX_REFUNGIBLE_PIECES,1612 Error::<T>::WrongRefungiblePieces1613 );1614 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1615 } else {1616 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1617 }1618 }1619 _ => {1620 fail!(Error::<T>::UnexpectedCollectionType);1621 }1622 };16231624 Ok(())1625 }16261627 fn create_item_no_validation(1628 collection: &CollectionHandle<T>,1629 owner: &T::CrossAccountId,1630 data: CreateItemData,1631 ) -> DispatchResult {1632 match data {1633 CreateItemData::NFT(data) => {1634 let item = NftItemType {1635 owner: owner.clone(),1636 const_data: data.const_data.into_inner(),1637 variable_data: data.variable_data.into_inner(),1638 };16391640 Self::add_nft_item(collection, item)?;1641 }1642 CreateItemData::Fungible(data) => {1643 Self::add_fungible_item(collection, owner, data.value)?;1644 }1645 CreateItemData::ReFungible(data) => {1646 let owner_list = vec![Ownership {1647 owner: owner.clone(),1648 fraction: data.pieces,1649 }];16501651 let item = ReFungibleItemType {1652 owner: owner_list,1653 const_data: data.const_data.into_inner(),1654 variable_data: data.variable_data.into_inner(),1655 };16561657 Self::add_refungible_item(collection, item)?;1658 }1659 };16601661 Ok(())1662 }16631664 fn add_fungible_item(1665 collection: &CollectionHandle<T>,1666 owner: &T::CrossAccountId,1667 value: u128,1668 ) -> DispatchResult {1669 let collection_id = collection.id;16701671 // Does new owner already have an account?1672 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16731674 // Mint1675 let item = FungibleItemType {1676 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1677 };1678 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16791680 // Update balance1681 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1682 .checked_add(value)1683 .ok_or(Error::<T>::NumOverflow)?;1684 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16851686 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1687 Ok(())1688 }16891690 fn add_refungible_item(1691 collection: &CollectionHandle<T>,1692 item: ReFungibleItemType<T::CrossAccountId>,1693 ) -> DispatchResult {1694 let collection_id = collection.id;16951696 let current_index = <ItemListIndex>::get(collection_id)1697 .checked_add(1)1698 .ok_or(Error::<T>::NumOverflow)?;1699 let itemcopy = item.clone();17001701 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1702 let item_owner = item.owner.first().expect("only one owner is defined");17031704 let value = item_owner.fraction;1705 let owner = item_owner.owner.clone();17061707 Self::add_token_index(collection_id, current_index, &owner)?;17081709 <ItemListIndex>::insert(collection_id, current_index);1710 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17111712 // Update balance1713 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1714 .checked_add(value)1715 .ok_or(Error::<T>::NumOverflow)?;1716 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17171718 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1719 Ok(())1720 }17211722 fn add_nft_item(1723 collection: &CollectionHandle<T>,1724 item: NftItemType<T::CrossAccountId>,1725 ) -> DispatchResult {1726 let collection_id = collection.id;17271728 let current_index = <ItemListIndex>::get(collection_id)1729 .checked_add(1)1730 .ok_or(Error::<T>::NumOverflow)?;17311732 let item_owner = item.owner.clone();1733 Self::add_token_index(collection_id, current_index, &item.owner)?;17341735 <ItemListIndex>::insert(collection_id, current_index);1736 <NftItemList<T>>::insert(collection_id, current_index, item);17371738 // Update balance1739 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1740 .checked_add(1)1741 .ok_or(Error::<T>::NumOverflow)?;1742 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17431744 collection.log(ERC721Events::Transfer {1745 from: H160::default(),1746 to: *item_owner.as_eth(),1747 token_id: current_index.into(),1748 })?;1749 Self::deposit_event(RawEvent::ItemCreated(1750 collection_id,1751 current_index,1752 item_owner,1753 ));1754 Ok(())1755 }17561757 fn burn_refungible_item(1758 collection: &CollectionHandle<T>,1759 item_id: TokenId,1760 owner: &T::CrossAccountId,1761 ) -> DispatchResult {1762 let collection_id = collection.id;17631764 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1765 .ok_or(Error::<T>::TokenNotFound)?;1766 let rft_balance = token1767 .owner1768 .iter()1769 .find(|&i| i.owner == *owner)1770 .ok_or(Error::<T>::TokenNotFound)?;1771 Self::remove_token_index(collection_id, item_id, owner)?;17721773 // update balance1774 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1775 .checked_sub(rft_balance.fraction)1776 .ok_or(Error::<T>::NumOverflow)?;1777 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17781779 // Re-create owners list with sender removed1780 let index = token1781 .owner1782 .iter()1783 .position(|i| i.owner == *owner)1784 .expect("owned item is exists");1785 token.owner.remove(index);1786 let owner_count = token.owner.len();17871788 // Burn the token completely if this was the last (only) owner1789 if owner_count == 0 {1790 <ReFungibleItemList<T>>::remove(collection_id, item_id);1791 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1792 } else {1793 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1794 }17951796 Ok(())1797 }17981799 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1800 let collection_id = collection.id;18011802 let item =1803 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1804 Self::remove_token_index(collection_id, item_id, &item.owner)?;18051806 // update balance1807 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1808 .checked_sub(1)1809 .ok_or(Error::<T>::NumOverflow)?;1810 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1811 <NftItemList<T>>::remove(collection_id, item_id);1812 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18131814 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1815 Ok(())1816 }18171818 fn burn_fungible_item(1819 owner: &T::CrossAccountId,1820 collection: &CollectionHandle<T>,1821 value: u128,1822 ) -> DispatchResult {1823 let collection_id = collection.id;18241825 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1826 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18271828 // update balance1829 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830 .checked_sub(value)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834 if balance.value - value > 0 {1835 balance.value -= value;1836 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1837 } else {1838 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1839 }18401841 collection.log(ERC20Events::Transfer {1842 from: *owner.as_eth(),1843 to: H160::default(),1844 value: value.into(),1845 })?;1846 Ok(())1847 }18481849 pub fn get_collection(1850 collection_id: CollectionId,1851 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1852 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1853 }18541855 fn check_owner_permissions(1856 target_collection: &CollectionHandle<T>,1857 subject: &T::AccountId,1858 ) -> DispatchResult {1859 ensure!(1860 *subject == target_collection.owner,1861 Error::<T>::NoPermission1862 );18631864 Ok(())1865 }18661867 fn is_owner_or_admin_permissions(1868 collection: &CollectionHandle<T>,1869 subject: &T::CrossAccountId,1870 ) -> bool {1871 *subject.as_sub() == collection.owner1872 || <AdminList<T>>::get(collection.id).contains(subject)1873 }18741875 fn check_owner_or_admin_permissions(1876 collection: &CollectionHandle<T>,1877 subject: &T::CrossAccountId,1878 ) -> DispatchResult {1879 ensure!(1880 Self::is_owner_or_admin_permissions(collection, subject),1881 Error::<T>::NoPermission1882 );18831884 Ok(())1885 }18861887 fn owned_amount(1888 subject: &T::CrossAccountId,1889 target_collection: &CollectionHandle<T>,1890 item_id: TokenId,1891 ) -> Option<u128> {1892 let collection_id = target_collection.id;18931894 match target_collection.mode {1895 CollectionMode::NFT => {1896 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1897 }1898 CollectionMode::Fungible(_) => {1899 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1900 }1901 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1902 .owner1903 .iter()1904 .find(|i| i.owner == *subject)1905 .map(|i| i.fraction),1906 CollectionMode::Invalid => None,1907 }1908 }19091910 fn is_item_owner(1911 subject: &T::CrossAccountId,1912 target_collection: &CollectionHandle<T>,1913 item_id: TokenId,1914 ) -> bool {1915 match target_collection.mode {1916 CollectionMode::Fungible(_) => true,1917 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1918 }1919 }19201921 fn check_white_list(1922 collection: &CollectionHandle<T>,1923 address: &T::CrossAccountId,1924 ) -> DispatchResult {1925 let collection_id = collection.id;19261927 let mes = Error::<T>::AddresNotInWhiteList;1928 ensure!(1929 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1930 mes1931 );19321933 Ok(())1934 }19351936 /// Check if token exists. In case of Fungible, check if there is an entry for1937 /// the owner in fungible balances double map1938 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1939 let collection_id = target_collection.id;1940 let exists = match target_collection.mode {1941 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1942 CollectionMode::Fungible(_) => true,1943 CollectionMode::ReFungible => {1944 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1945 }1946 _ => false,1947 };19481949 ensure!(exists, Error::<T>::TokenNotFound);1950 Ok(())1951 }19521953 fn transfer_fungible(1954 collection: &CollectionHandle<T>,1955 value: u128,1956 owner: &T::CrossAccountId,1957 recipient: &T::CrossAccountId,1958 ) -> DispatchResult {1959 let collection_id = collection.id;19601961 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1962 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19631964 // Send balance to recipient (updates balanceOf of recipient)1965 Self::add_fungible_item(collection, recipient, value)?;19661967 // update balanceOf of sender1968 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19691970 // Reduce or remove sender1971 if balance.value == value {1972 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1973 } else {1974 balance.value -= value;1975 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1976 }19771978 collection.log(ERC20Events::Transfer {1979 from: *owner.as_eth(),1980 to: *recipient.as_eth(),1981 value: value.into(),1982 })?;1983 Self::deposit_event(RawEvent::Transfer(1984 collection.id,1985 1,1986 owner.clone(),1987 recipient.clone(),1988 value,1989 ));19901991 Ok(())1992 }19931994 fn transfer_refungible(1995 collection: &CollectionHandle<T>,1996 item_id: TokenId,1997 value: u128,1998 owner: T::CrossAccountId,1999 new_owner: T::CrossAccountId,2000 ) -> DispatchResult {2001 let collection_id = collection.id;2002 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2003 .ok_or(Error::<T>::TokenNotFound)?;20042005 let item = full_item2006 .owner2007 .iter()2008 .find(|i| i.owner == owner)2009 .ok_or(Error::<T>::TokenNotFound)?;2010 let amount = item.fraction;20112012 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20132014 // update balance2015 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2016 .checked_sub(value)2017 .ok_or(Error::<T>::NumOverflow)?;2018 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20192020 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2021 .checked_add(value)2022 .ok_or(Error::<T>::NumOverflow)?;2023 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20242025 let old_owner = item.owner.clone();2026 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20272028 let mut new_full_item = full_item.clone();2029 // transfer2030 if amount == value && !new_owner_has_account {2031 // change owner2032 // new owner do not have account2033 new_full_item2034 .owner2035 .iter_mut()2036 .find(|i| i.owner == owner)2037 .expect("old owner does present in refungible")2038 .owner = new_owner.clone();2039 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20402041 // update index collection2042 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2043 } else {2044 new_full_item2045 .owner2046 .iter_mut()2047 .find(|i| i.owner == owner)2048 .expect("old owner does present in refungible")2049 .fraction -= value;20502051 // separate amount2052 if new_owner_has_account {2053 // new owner has account2054 new_full_item2055 .owner2056 .iter_mut()2057 .find(|i| i.owner == new_owner)2058 .expect("new owner has account")2059 .fraction += value;2060 } else {2061 // new owner do not have account2062 new_full_item.owner.push(Ownership {2063 owner: new_owner.clone(),2064 fraction: value,2065 });2066 Self::add_token_index(collection_id, item_id, &new_owner)?;2067 }20682069 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2070 }20712072 Self::deposit_event(RawEvent::Transfer(2073 collection.id,2074 item_id,2075 owner,2076 new_owner,2077 amount,2078 ));20792080 Ok(())2081 }20822083 fn transfer_nft(2084 collection: &CollectionHandle<T>,2085 item_id: TokenId,2086 sender: T::CrossAccountId,2087 new_owner: T::CrossAccountId,2088 ) -> DispatchResult {2089 let collection_id = collection.id;2090 let mut item =2091 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20922093 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20942095 // update balance2096 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2097 .checked_sub(1)2098 .ok_or(Error::<T>::NumOverflow)?;2099 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21002101 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2102 .checked_add(1)2103 .ok_or(Error::<T>::NumOverflow)?;2104 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21052106 // change owner2107 let old_owner = item.owner.clone();2108 item.owner = new_owner.clone();2109 <NftItemList<T>>::insert(collection_id, item_id, item);21102111 // update index collection2112 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21132114 collection.log(ERC721Events::Transfer {2115 from: *sender.as_eth(),2116 to: *new_owner.as_eth(),2117 token_id: item_id.into(),2118 })?;2119 Self::deposit_event(RawEvent::Transfer(2120 collection.id,2121 item_id,2122 sender,2123 new_owner,2124 1,2125 ));21262127 Ok(())2128 }21292130 fn set_re_fungible_variable_data(2131 collection: &CollectionHandle<T>,2132 item_id: TokenId,2133 data: Vec<u8>,2134 ) -> DispatchResult {2135 let collection_id = collection.id;2136 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2137 .ok_or(Error::<T>::TokenNotFound)?;21382139 item.variable_data = data;21402141 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21422143 Ok(())2144 }21452146 fn set_nft_variable_data(2147 collection: &CollectionHandle<T>,2148 item_id: TokenId,2149 data: Vec<u8>,2150 ) -> DispatchResult {2151 let collection_id = collection.id;2152 let mut item =2153 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21542155 item.variable_data = data;21562157 <NftItemList<T>>::insert(collection_id, item_id, item);21582159 Ok(())2160 }21612162 #[allow(dead_code)]2163 fn init_collection(item: &Collection<T>) {2164 // check params2165 assert!(2166 item.decimal_points <= MAX_DECIMAL_POINTS,2167 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2168 );2169 assert!(2170 item.name.len() <= 64,2171 "Collection name can not be longer than 63 char"2172 );2173 assert!(2174 item.name.len() <= 256,2175 "Collection description can not be longer than 255 char"2176 );2177 assert!(2178 item.token_prefix.len() <= 16,2179 "Token prefix can not be longer than 15 char"2180 );21812182 // Generate next collection ID2183 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21842185 CreatedCollectionCount::put(next_id);2186 }21872188 #[allow(dead_code)]2189 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2190 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21912192 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21932194 <ItemListIndex>::insert(collection_id, current_index);21952196 // Update balance2197 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2198 .checked_add(1)2199 .unwrap();2200 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2201 }22022203 #[allow(dead_code)]2204 fn init_fungible_token(2205 collection_id: CollectionId,2206 owner: &T::CrossAccountId,2207 item: &FungibleItemType,2208 ) {2209 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22102211 Self::add_token_index(collection_id, current_index, owner).unwrap();22122213 <ItemListIndex>::insert(collection_id, current_index);22142215 // Update balance2216 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2217 .checked_add(item.value)2218 .unwrap();2219 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2220 }22212222 #[allow(dead_code)]2223 fn init_refungible_token(2224 collection_id: CollectionId,2225 item: &ReFungibleItemType<T::CrossAccountId>,2226 ) {2227 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22282229 let value = item.owner.first().unwrap().fraction;2230 let owner = item.owner.first().unwrap().owner.clone();22312232 Self::add_token_index(collection_id, current_index, &owner).unwrap();22332234 <ItemListIndex>::insert(collection_id, current_index);22352236 // Update balance2237 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2238 .checked_add(value)2239 .unwrap();2240 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241 }22422243 fn add_token_index(2244 collection_id: CollectionId,2245 item_index: TokenId,2246 owner: &T::CrossAccountId,2247 ) -> DispatchResult {2248 // add to account limit2249 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2250 // bound Owned tokens by a single address2251 let count = <AccountItemCount<T>>::get(owner.as_sub());2252 ensure!(2253 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2254 Error::<T>::AddressOwnershipLimitExceeded2255 );22562257 <AccountItemCount<T>>::insert(2258 owner.as_sub(),2259 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2260 );2261 } else {2262 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2263 }22642265 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2266 if list_exists {2267 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2268 let item_contains = list.contains(&item_index.clone());22692270 if !item_contains {2271 list.push(item_index);2272 }22732274 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2275 } else {2276 let itm = vec![item_index];2277 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2278 }22792280 Ok(())2281 }22822283 fn remove_token_index(2284 collection_id: CollectionId,2285 item_index: TokenId,2286 owner: &T::CrossAccountId,2287 ) -> DispatchResult {2288 // update counter2289 <AccountItemCount<T>>::insert(2290 owner.as_sub(),2291 <AccountItemCount<T>>::get(owner.as_sub())2292 .checked_sub(1)2293 .ok_or(Error::<T>::NumOverflow)?,2294 );22952296 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2297 if list_exists {2298 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2299 let item_contains = list.contains(&item_index.clone());23002301 if item_contains {2302 list.retain(|&item| item != item_index);2303 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2304 }2305 }23062307 Ok(())2308 }23092310 fn move_token_index(2311 collection_id: CollectionId,2312 item_index: TokenId,2313 old_owner: &T::CrossAccountId,2314 new_owner: &T::CrossAccountId,2315 ) -> DispatchResult {2316 Self::remove_token_index(collection_id, item_index, old_owner)?;2317 Self::add_token_index(collection_id, item_index, new_owner)?;23182319 Ok(())2320 }2321}23222323sp_api::decl_runtime_apis! {2324 pub trait NftApi {2325 /// Used for ethereum integration2326 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2327 }2328}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_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, 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};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, MAX_TOKEN_PREFIX_LENGTH, MAX_COLLECTION_NAME_LENGTH,44 MAX_COLLECTION_DESCRIPTION_LENGTH, AccessMode, Collection, CreateItemData, CollectionLimits,45 CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,46 FungibleItemType, ReFungibleItemType,47};4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;66pub mod weights;67use weights::WeightInfo;6869decl_error! {70 /// Error for non-fungible-token module.71 pub enum Error for Module<T: Config> {72 /// Total collections bound exceeded.73 TotalCollectionsLimitExceeded,74 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.75 CollectionDecimalPointLimitExceeded,76 /// Collection name can not be longer than 63 char.77 CollectionNameLimitExceeded,78 /// Collection description can not be longer than 255 char.79 CollectionDescriptionLimitExceeded,80 /// Token prefix can not be longer than 15 char.81 CollectionTokenPrefixLimitExceeded,82 /// This collection does not exist.83 CollectionNotFound,84 /// Item not exists.85 TokenNotFound,86 /// Admin not found87 AdminNotFound,88 /// Arithmetic calculation overflow.89 NumOverflow,90 /// Account already has admin role.91 AlreadyAdmin,92 /// You do not own this collection.93 NoPermission,94 /// This address is not set as sponsor, use setCollectionSponsor first.95 ConfirmUnsetSponsorFail,96 /// Collection is not in mint mode.97 PublicMintingNotAllowed,98 /// Sender parameter and item owner must be equal.99 MustBeTokenOwner,100 /// Item balance not enough.101 TokenValueTooLow,102 /// Size of item is too large.103 NftSizeLimitExceeded,104 /// No approve found105 ApproveNotFound,106 /// Requested value more than approved.107 TokenValueNotEnough,108 /// Only approved addresses can call this method.109 ApproveRequired,110 /// Address is not in white list.111 AddresNotInWhiteList,112 /// Number of collection admins bound exceeded.113 CollectionAdminsLimitExceeded,114 /// Owned tokens by a single address bound exceeded.115 AddressOwnershipLimitExceeded,116 /// Length of items properties must be greater than 0.117 EmptyArgument,118 /// const_data exceeded data limit.119 TokenConstDataLimitExceeded,120 /// variable_data exceeded data limit.121 TokenVariableDataLimitExceeded,122 /// Not NFT item data used to mint in NFT collection.123 NotNftDataUsedToMintNftCollectionToken,124 /// Not Fungible item data used to mint in Fungible collection.125 NotFungibleDataUsedToMintFungibleCollectionToken,126 /// Not Re Fungible item data used to mint in Re Fungible collection.127 NotReFungibleDataUsedToMintReFungibleCollectionToken,128 /// Unexpected collection type.129 UnexpectedCollectionType,130 /// Can't store metadata in fungible tokens.131 CantStoreMetadataInFungibleTokens,132 /// Collection token limit exceeded133 CollectionTokenLimitExceeded,134 /// Account token limit exceeded per collection135 AccountTokenLimitExceeded,136 /// Collection limit bounds per collection exceeded137 CollectionLimitBoundsExceeded,138 /// Tried to enable permissions which are only permitted to be disabled139 OwnerPermissionsCantBeReverted,140 /// Schema data size limit bound exceeded141 SchemaDataLimitExceeded,142 /// Maximum refungibility exceeded143 WrongRefungiblePieces,144 /// createRefungible should be called with one owner145 BadCreateRefungibleCall,146 /// Gas limit exceeded147 OutOfGas,148 /// Collection settings not allowing items transferring149 TransferNotAllowed,150 }151}152153#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]154pub struct CollectionHandle<T: Config> {155 pub id: CollectionId,156 collection: Collection<T>,157 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,158}159impl<T: Config> CollectionHandle<T> {160 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {161 <CollectionById<T>>::get(id).map(|collection| Self {162 id,163 collection,164 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(165 eth::collection_id_to_address(id),166 gas_limit,167 ),168 })169 }170 pub fn get(id: CollectionId) -> Option<Self> {171 Self::get_with_gas_limit(id, u64::MAX)172 }173 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {174 self.recorder.log_sub(log)175 }176 fn consume_gas(&self, gas: u64) -> DispatchResult {177 self.recorder.consume_gas_sub(gas)178 }179 pub fn submit_logs(self) -> DispatchResult {180 self.recorder.submit_logs()181 }182 pub fn save(self) -> DispatchResult {183 self.recorder.submit_logs()?;184 <CollectionById<T>>::insert(self.id, self.collection);185 Ok(())186 }187}188impl<T: Config> Deref for CollectionHandle<T> {189 type Target = Collection<T>;190191 fn deref(&self) -> &Self::Target {192 &self.collection193 }194}195196impl<T: Config> DerefMut for CollectionHandle<T> {197 fn deref_mut(&mut self) -> &mut Self::Target {198 &mut self.collection199 }200}201202pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {203 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;204205 /// Weight information for extrinsics in this pallet.206 type WeightInfo: WeightInfo;207208 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;209 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;210211 type CrossAccountId: CrossAccountId<Self::AccountId>;212 type Currency: Currency<Self::AccountId>;213 type CollectionCreationPrice: Get<214 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,215 >;216 type TreasuryAccountId: Get<Self::AccountId>;217}218219type SelfWeightOf<T> = <T as Config>::WeightInfo;220221trait WeightInfoHelpers: WeightInfo {222 fn transfer() -> Weight {223 Self::transfer_nft()224 .max(Self::transfer_fungible())225 .max(Self::transfer_refungible())226 }227 fn transfer_from() -> Weight {228 Self::transfer_from_nft()229 .max(Self::transfer_from_fungible())230 .max(Self::transfer_from_refungible())231 }232 fn approve() -> Weight {233 // TODO: refungible, fungible234 Self::approve_nft()235 }236 fn set_variable_meta_data(data: u32) -> Weight {237 // TODO: refungible238 Self::set_variable_meta_data_nft(data)239 }240 fn create_item(data: u32) -> Weight {241 Self::create_item_nft(data)242 .max(Self::create_item_fungible())243 .max(Self::create_item_refungible(data))244 }245 fn burn_item() -> Weight {246 // TODO: refungible, fungible247 Self::burn_item_nft()248 }249}250impl<T: WeightInfo> WeightInfoHelpers for T {}251252// # Used definitions253//254// ## User control levels255//256// chain-controlled - key is uncontrolled by user257// i.e autoincrementing index258// can use non-cryptographic hash259// real - key is controlled by user260// but it is hard to generate enough colliding values, i.e owner of signed txs261// can use non-cryptographic hash262// controlled - key is completly controlled by users263// i.e maps with mutable keys264// should use cryptographic hash265//266// ## User control level downgrade reasons267//268// ?1 - chain-controlled -> controlled269// collections/tokens can be destroyed, resulting in massive holes270// ?2 - chain-controlled -> controlled271// same as ?1, but can be only added, resulting in easier exploitation272// ?3 - real -> controlled273// no confirmation required, so addresses can be easily generated274decl_storage! {275 trait Store for Module<T: Config> as Nft {276277 //#region Private members278 /// Id of next collection279 CreatedCollectionCount: u32;280 /// Used for migrations281 ChainVersion: u64;282 /// Id of last collection token283 /// Collection id (controlled?1)284 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;285 //#endregion286287 //#region Bound counters288 /// Amount of collections destroyed, used for total amount tracking with289 /// CreatedCollectionCount290 DestroyedCollectionCount: u32;291 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)292 /// Account id (real)293 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;294 //#endregion295296 //#region Basic collections297 /// Collection info298 /// Collection id (controlled?1)299 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;300 /// List of collection admins301 /// Collection id (controlled?2)302 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;303 /// Whitelisted collection users304 /// Collection id (controlled?2), user id (controlled?3)305 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;306 //#endregion307308 /// How many of collection items user have309 /// Collection id (controlled?2), account id (real)310 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;311312 /// Amount of items which spender can transfer out of owners account (via transferFrom)313 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))314 /// TODO: Off chain worker should remove from this map when token gets removed315 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;316317 //#region Item collections318 /// Collection id (controlled?2), token id (controlled?1)319 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;320 /// Collection id (controlled?2), owner (controlled?2)321 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;322 /// Collection id (controlled?2), token id (controlled?1)323 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;324 //#endregion325326 //#region Index list327 /// Collection id (controlled?2), tokens owner (controlled?2)328 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;329 //#endregion330331 //#region Tokens transfer rate limit baskets332 /// (Collection id (controlled?2), who created (real))333 /// TODO: Off chain worker should remove from this map when collection gets removed334 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;335 /// Collection id (controlled?2), token id (controlled?2)336 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;337 /// Collection id (controlled?2), owning user (real)338 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;339 /// Collection id (controlled?2), token id (controlled?2)340 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;341 //#endregion342343 /// Variable metadata sponsoring344 /// Collection id (controlled?2), token id (controlled?2)345 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;346 }347 add_extra_genesis {348 build(|config: &GenesisConfig<T>| {349 // Modification of storage350 for (_num, _c) in &config.collection_id {351 <Module<T>>::init_collection(_c);352 }353354 for (_num, _c, _i) in &config.nft_item_id {355 <Module<T>>::init_nft_token(*_c, _i);356 }357358 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {359 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);360 }361362 for (_num, _c, _i) in &config.refungible_item_id {363 <Module<T>>::init_refungible_token(*_c, _i);364 }365 })366 }367}368369decl_event!(370 pub enum Event<T>371 where372 AccountId = <T as frame_system::Config>::AccountId,373 CrossAccountId = <T as Config>::CrossAccountId,374 {375 /// New collection was created376 ///377 /// # Arguments378 ///379 /// * collection_id: Globally unique identifier of newly created collection.380 ///381 /// * mode: [CollectionMode] converted into u8.382 ///383 /// * account_id: Collection owner.384 CollectionCreated(CollectionId, u8, AccountId),385386 /// New item was created.387 ///388 /// # Arguments389 ///390 /// * collection_id: Id of the collection where item was created.391 ///392 /// * item_id: Id of an item. Unique within the collection.393 ///394 /// * recipient: Owner of newly created item395 ItemCreated(CollectionId, TokenId, CrossAccountId),396397 /// Collection item was burned.398 ///399 /// # Arguments400 ///401 /// collection_id.402 ///403 /// item_id: Identifier of burned NFT.404 ItemDestroyed(CollectionId, TokenId),405406 /// Item was transferred407 ///408 /// * collection_id: Id of collection to which item is belong409 ///410 /// * item_id: Id of an item411 ///412 /// * sender: Original owner of item413 ///414 /// * recipient: New owner of item415 ///416 /// * amount: Always 1 for NFT417 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),418419 /// * collection_id420 ///421 /// * item_id422 ///423 /// * sender424 ///425 /// * spender426 ///427 /// * amount428 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),429 }430);431432decl_module! {433 pub struct Module<T: Config> for enum Call434 where435 origin: T::Origin436 {437 fn deposit_event() = default;438 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;439 type Error = Error<T>;440441 fn on_initialize(_now: T::BlockNumber) -> Weight {442 0443 }444445 /// 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 and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.446 ///447 /// # Permissions448 ///449 /// * Anyone.450 ///451 /// # Arguments452 ///453 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.454 ///455 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.456 ///457 /// * token_prefix: UTF-8 string with token prefix.458 ///459 /// * mode: [CollectionMode] collection type and type dependent data.460 // returns collection ID461 #[weight = <SelfWeightOf<T>>::create_collection()]462 #[transactional]463 pub fn create_collection(origin,464 collection_name: Vec<u16>,465 collection_description: Vec<u16>,466 token_prefix: Vec<u8>,467 mode: CollectionMode) -> DispatchResult {468469 // Anyone can create a collection470 let who = ensure_signed(origin)?;471472 // Take a (non-refundable) deposit of collection creation473 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();474 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(475 &T::TreasuryAccountId::get(),476 T::CollectionCreationPrice::get(),477 ));478 <T as Config>::Currency::settle(479 &who,480 imbalance,481 WithdrawReasons::TRANSFER,482 ExistenceRequirement::KeepAlive,483 ).map_err(|_| Error::<T>::NoPermission)?;484485 let decimal_points = match mode {486 CollectionMode::Fungible(points) => points,487 _ => 0488 };489490 let created_count = CreatedCollectionCount::get();491 let destroyed_count = DestroyedCollectionCount::get();492493 // bound Total number of collections494 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);495496 // check params497 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);498 ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);499 ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);500 ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);501502 // Generate next collection ID503 let next_id = created_count504 .checked_add(1)505 .ok_or(Error::<T>::NumOverflow)?;506507 CreatedCollectionCount::put(next_id);508509 let limits = CollectionLimits {510 sponsored_data_size: CUSTOM_DATA_LIMIT,511 ..Default::default()512 };513514 // Create new collection515 let new_collection = Collection {516 owner: who.clone(),517 name: collection_name,518 mode: mode.clone(),519 mint_mode: false,520 access: AccessMode::Normal,521 description: collection_description,522 decimal_points,523 token_prefix,524 offchain_schema: Vec::new(),525 schema_version: SchemaVersion::ImageURL,526 sponsorship: SponsorshipState::Disabled,527 variable_on_chain_schema: Vec::new(),528 const_on_chain_schema: Vec::new(),529 limits,530 transfers_enabled: true,531 };532533 // Add new collection to map534 <CollectionById<T>>::insert(next_id, new_collection);535536 // call event537 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));538539 Ok(())540 }541542 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.543 ///544 /// # Permissions545 ///546 /// * Collection Owner.547 ///548 /// # Arguments549 ///550 /// * collection_id: collection to destroy.551 #[weight = <SelfWeightOf<T>>::destroy_collection()]552 #[transactional]553 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {554555 let sender = ensure_signed(origin)?;556 let collection = Self::get_collection(collection_id)?;557 Self::check_owner_permissions(&collection, &sender)?;558 if !collection.limits.owner_can_destroy {559 fail!(Error::<T>::NoPermission);560 }561562 <AddressTokens<T>>::remove_prefix(collection_id, None);563 <Allowances<T>>::remove_prefix(collection_id, None);564 <Balance<T>>::remove_prefix(collection_id, None);565 <ItemListIndex>::remove(collection_id);566 <AdminList<T>>::remove(collection_id);567 <CollectionById<T>>::remove(collection_id);568 <WhiteList<T>>::remove_prefix(collection_id, None);569570 <NftItemList<T>>::remove_prefix(collection_id, None);571 <FungibleItemList<T>>::remove_prefix(collection_id, None);572 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);573574 <NftTransferBasket<T>>::remove_prefix(collection_id, None);575 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);576 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);577578 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);579580 DestroyedCollectionCount::put(DestroyedCollectionCount::get()581 .checked_add(1)582 .ok_or(Error::<T>::NumOverflow)?);583584 Ok(())585 }586587 /// Add an address to white list.588 ///589 /// # Permissions590 ///591 /// * Collection Owner592 /// * Collection Admin593 ///594 /// # Arguments595 ///596 /// * collection_id.597 ///598 /// * address.599 #[weight = <SelfWeightOf<T>>::add_to_white_list()]600 #[transactional]601 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{602603 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);604 let collection = Self::get_collection(collection_id)?;605606 Self::toggle_white_list_internal(607 &sender,608 &collection,609 &address,610 true,611 )?;612613 Ok(())614 }615616 /// Remove an address from white list.617 ///618 /// # Permissions619 ///620 /// * Collection Owner621 /// * Collection Admin622 ///623 /// # Arguments624 ///625 /// * collection_id.626 ///627 /// * address.628 #[weight = <SelfWeightOf<T>>::remove_from_white_list()]629 #[transactional]630 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{631632 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);633 let collection = Self::get_collection(collection_id)?;634635 Self::toggle_white_list_internal(636 &sender,637 &collection,638 &address,639 false,640 )?;641642 Ok(())643 }644645 /// Toggle between normal and white list access for the methods with access for `Anyone`.646 ///647 /// # Permissions648 ///649 /// * Collection Owner.650 ///651 /// # Arguments652 ///653 /// * collection_id.654 ///655 /// * mode: [AccessMode]656 #[weight = <SelfWeightOf<T>>::set_public_access_mode()]657 #[transactional]658 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult659 {660 let sender = ensure_signed(origin)?;661662 let mut target_collection = Self::get_collection(collection_id)?;663 Self::check_owner_permissions(&target_collection, &sender)?;664 target_collection.access = mode;665 target_collection.save()666 }667668 /// Allows Anyone to create tokens if:669 /// * White List is enabled, and670 /// * Address is added to white list, and671 /// * This method was called with True parameter672 ///673 /// # Permissions674 /// * Collection Owner675 ///676 /// # Arguments677 ///678 /// * collection_id.679 ///680 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.681 #[weight = <SelfWeightOf<T>>::set_mint_permission()]682 #[transactional]683 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult684 {685 let sender = ensure_signed(origin)?;686687 let mut target_collection = Self::get_collection(collection_id)?;688 Self::check_owner_permissions(&target_collection, &sender)?;689 target_collection.mint_mode = mint_permission;690 target_collection.save()691 }692693 /// Change the owner of the collection.694 ///695 /// # Permissions696 ///697 /// * Collection Owner.698 ///699 /// # Arguments700 ///701 /// * collection_id.702 ///703 /// * new_owner.704 #[weight = <SelfWeightOf<T>>::change_collection_owner()]705 #[transactional]706 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {707708 let sender = ensure_signed(origin)?;709 let mut target_collection = Self::get_collection(collection_id)?;710 Self::check_owner_permissions(&target_collection, &sender)?;711 target_collection.owner = new_owner;712 target_collection.save()713 }714715 /// Adds an admin of the Collection.716 /// 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.717 ///718 /// # Permissions719 ///720 /// * Collection Owner.721 /// * Collection Admin.722 ///723 /// # Arguments724 ///725 /// * collection_id: ID of the Collection to add admin for.726 ///727 /// * new_admin_id: Address of new admin to add.728 #[weight = <SelfWeightOf<T>>::add_collection_admin()]729 #[transactional]730 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {731 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);732 let collection = Self::get_collection(collection_id)?;733 Self::check_owner_or_admin_permissions(&collection, &sender)?;734 let mut admin_arr = <AdminList<T>>::get(collection_id);735736 match admin_arr.binary_search(&new_admin_id) {737 Ok(_) => {},738 Err(idx) => {739 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);740 admin_arr.insert(idx, new_admin_id);741 <AdminList<T>>::insert(collection_id, admin_arr);742 }743 }744 Ok(())745 }746747 /// 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.748 ///749 /// # Permissions750 ///751 /// * Collection Owner.752 /// * Collection Admin.753 ///754 /// # Arguments755 ///756 /// * collection_id: ID of the Collection to remove admin for.757 ///758 /// * account_id: Address of admin to remove.759 #[weight = <SelfWeightOf<T>>::remove_collection_admin()]760 #[transactional]761 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {762 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);763 let collection = Self::get_collection(collection_id)?;764 Self::check_owner_or_admin_permissions(&collection, &sender)?;765 let mut admin_arr = <AdminList<T>>::get(collection_id);766767 if let Ok(idx) = admin_arr.binary_search(&account_id) {768 admin_arr.remove(idx);769 <AdminList<T>>::insert(collection_id, admin_arr);770 }771 Ok(())772 }773774 /// # Permissions775 ///776 /// * Collection Owner777 ///778 /// # Arguments779 ///780 /// * collection_id.781 ///782 /// * new_sponsor.783 #[weight = <SelfWeightOf<T>>::set_collection_sponsor()]784 #[transactional]785 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {786 let sender = ensure_signed(origin)?;787 let mut target_collection = Self::get_collection(collection_id)?;788 Self::check_owner_permissions(&target_collection, &sender)?;789790 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);791 target_collection.save()792 }793794 /// # Permissions795 ///796 /// * Sponsor.797 ///798 /// # Arguments799 ///800 /// * collection_id.801 #[weight = <SelfWeightOf<T>>::confirm_sponsorship()]802 #[transactional]803 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {804 let sender = ensure_signed(origin)?;805806 let mut target_collection = Self::get_collection(collection_id)?;807 ensure!(808 target_collection.sponsorship.pending_sponsor() == Some(&sender),809 Error::<T>::ConfirmUnsetSponsorFail810 );811812 target_collection.sponsorship = SponsorshipState::Confirmed(sender);813 target_collection.save()814 }815816 /// Switch back to pay-per-own-transaction model.817 ///818 /// # Permissions819 ///820 /// * Collection owner.821 ///822 /// # Arguments823 ///824 /// * collection_id.825 #[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]826 #[transactional]827 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {828 let sender = ensure_signed(origin)?;829830 let mut target_collection = Self::get_collection(collection_id)?;831 Self::check_owner_permissions(&target_collection, &sender)?;832833 target_collection.sponsorship = SponsorshipState::Disabled;834 target_collection.save()835 }836837 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.838 ///839 /// # Permissions840 ///841 /// * Collection Owner.842 /// * Collection Admin.843 /// * Anyone if844 /// * White List is enabled, and845 /// * Address is added to white list, and846 /// * MintPermission is enabled (see SetMintPermission method)847 ///848 /// # Arguments849 ///850 /// * collection_id: ID of the collection.851 ///852 /// * owner: Address, initial owner of the NFT.853 ///854 /// * data: Token data to store on chain.855 // #[weight =856 // (130_000_000 as Weight)857 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))858 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))859 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]860861 #[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]862 #[transactional]863 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {864 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);865 let collection = Self::get_collection(collection_id)?;866867 Self::create_item_internal(&sender, &collection, &owner, data)?;868869 collection.submit_logs()870 }871872 /// This method creates multiple items in a collection created with CreateCollection method.873 ///874 /// # Permissions875 ///876 /// * Collection Owner.877 /// * Collection Admin.878 /// * Anyone if879 /// * White List is enabled, and880 /// * Address is added to white list, and881 /// * MintPermission is enabled (see SetMintPermission method)882 ///883 /// # Arguments884 ///885 /// * collection_id: ID of the collection.886 ///887 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].888 ///889 /// * owner: Address, initial owner of the NFT.890 #[weight = <SelfWeightOf<T>>::create_item(items_data.iter()891 .map(|data| { data.data_size() as u32 })892 .sum())]893 #[transactional]894 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {895896 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);897 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);898 let collection = Self::get_collection(collection_id)?;899900 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;901902 collection.submit_logs()903 }904905 // TODO! transaction weight906907 /// Set transfers_enabled value for particular collection908 ///909 /// # Permissions910 ///911 /// * Collection Owner.912 ///913 /// # Arguments914 ///915 /// * collection_id: ID of the collection.916 ///917 /// * value: New flag value.918 #[weight = <SelfWeightOf<T>>::burn_item()]919 #[transactional]920 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {921922 let sender = ensure_signed(origin)?;923 let mut target_collection = Self::get_collection(collection_id)?;924925 Self::check_owner_permissions(&target_collection, &sender)?;926927 target_collection.transfers_enabled = value;928 target_collection.save()929 }930931 /// Destroys a concrete instance of NFT.932 ///933 /// # Permissions934 ///935 /// * Collection Owner.936 /// * Collection Admin.937 /// * Current NFT Owner.938 ///939 /// # Arguments940 ///941 /// * collection_id: ID of the collection.942 ///943 /// * item_id: ID of NFT to burn.944 #[weight = <SelfWeightOf<T>>::burn_item()]945 #[transactional]946 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {947948 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);949 let target_collection = Self::get_collection(collection_id)?;950951 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;952953 target_collection.submit_logs()954 }955956 /// Change ownership of the token.957 ///958 /// # Permissions959 ///960 /// * Collection Owner961 /// * Collection Admin962 /// * Current NFT owner963 ///964 /// # Arguments965 ///966 /// * recipient: Address of token recipient.967 ///968 /// * collection_id.969 ///970 /// * item_id: ID of the item971 /// * Non-Fungible Mode: Required.972 /// * Fungible Mode: Ignored.973 /// * Re-Fungible Mode: Required.974 ///975 /// * value: Amount to transfer.976 /// * Non-Fungible Mode: Ignored977 /// * Fungible Mode: Must specify transferred amount978 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)979 #[weight = <SelfWeightOf<T>>::transfer()]980 #[transactional]981 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {982 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);983 let collection = Self::get_collection(collection_id)?;984985 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;986987 collection.submit_logs()988 }989990 /// Set, change, or remove approved address to transfer the ownership of the NFT.991 ///992 /// # Permissions993 ///994 /// * Collection Owner995 /// * Collection Admin996 /// * Current NFT owner997 ///998 /// # Arguments999 ///1000 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1001 ///1002 /// * collection_id.1003 ///1004 /// * item_id: ID of the item.1005 #[weight = <SelfWeightOf<T>>::approve()]1006 #[transactional]1007 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1008 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1009 let collection = Self::get_collection(collection_id)?;10101011 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10121013 collection.submit_logs()1014 }10151016 /// 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.1017 ///1018 /// # Permissions1019 /// * Collection Owner1020 /// * Collection Admin1021 /// * Current NFT owner1022 /// * Address approved by current NFT owner1023 ///1024 /// # Arguments1025 ///1026 /// * from: Address that owns token.1027 ///1028 /// * recipient: Address of token recipient.1029 ///1030 /// * collection_id.1031 ///1032 /// * item_id: ID of the item.1033 ///1034 /// * value: Amount to transfer.1035 #[weight = <SelfWeightOf<T>>::transfer_from()]1036 #[transactional]1037 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1038 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1039 let collection = Self::get_collection(collection_id)?;10401041 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10421043 collection.submit_logs()1044 }1045 // #[weight = 0]1046 // // let no_perm_mes = "You do not have permissions to modify this collection";1047 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1048 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1049 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10501051 // // // on_nft_received call10521053 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10541055 // Ok(())1056 // }10571058 /// Set off-chain data schema.1059 ///1060 /// # Permissions1061 ///1062 /// * Collection Owner1063 /// * Collection Admin1064 ///1065 /// # Arguments1066 ///1067 /// * collection_id.1068 ///1069 /// * schema: String representing the offchain data schema.1070 #[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1071 #[transactional]1072 pub fn set_variable_meta_data (1073 origin,1074 collection_id: CollectionId,1075 item_id: TokenId,1076 data: Vec<u8>1077 ) -> DispatchResult {1078 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10791080 let collection = Self::get_collection(collection_id)?;10811082 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10831084 Ok(())1085 }10861087 /// Set schema standard1088 /// ImageURL1089 /// Unique1090 ///1091 /// # Permissions1092 ///1093 /// * Collection Owner1094 /// * Collection Admin1095 ///1096 /// # Arguments1097 ///1098 /// * collection_id.1099 ///1100 /// * schema: SchemaVersion: enum1101 #[weight = <SelfWeightOf<T>>::set_schema_version()]1102 #[transactional]1103 pub fn set_schema_version(1104 origin,1105 collection_id: CollectionId,1106 version: SchemaVersion1107 ) -> DispatchResult {1108 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1109 let mut target_collection = Self::get_collection(collection_id)?;1110 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1111 target_collection.schema_version = version;1112 target_collection.save()1113 }11141115 /// Set off-chain data schema.1116 ///1117 /// # Permissions1118 ///1119 /// * Collection Owner1120 /// * Collection Admin1121 ///1122 /// # Arguments1123 ///1124 /// * collection_id.1125 ///1126 /// * schema: String representing the offchain data schema.1127 #[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1128 #[transactional]1129 pub fn set_offchain_schema(1130 origin,1131 collection_id: CollectionId,1132 schema: Vec<u8>1133 ) -> DispatchResult {1134 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1135 let mut target_collection = Self::get_collection(collection_id)?;1136 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11371138 // check schema limit1139 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11401141 target_collection.offchain_schema = schema;1142 target_collection.save()1143 }11441145 /// Set const on-chain data schema.1146 ///1147 /// # Permissions1148 ///1149 /// * Collection Owner1150 /// * Collection Admin1151 ///1152 /// # Arguments1153 ///1154 /// * collection_id.1155 ///1156 /// * schema: String representing the const on-chain data schema.1157 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1158 #[transactional]1159 pub fn set_const_on_chain_schema (1160 origin,1161 collection_id: CollectionId,1162 schema: Vec<u8>1163 ) -> DispatchResult {1164 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1165 let mut target_collection = Self::get_collection(collection_id)?;1166 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11671168 // check schema limit1169 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11701171 target_collection.const_on_chain_schema = schema;1172 target_collection.save()1173 }11741175 /// Set variable on-chain data schema.1176 ///1177 /// # Permissions1178 ///1179 /// * Collection Owner1180 /// * Collection Admin1181 ///1182 /// # Arguments1183 ///1184 /// * collection_id.1185 ///1186 /// * schema: String representing the variable on-chain data schema.1187 #[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1188 #[transactional]1189 pub fn set_variable_on_chain_schema (1190 origin,1191 collection_id: CollectionId,1192 schema: Vec<u8>1193 ) -> DispatchResult {1194 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1195 let mut target_collection = Self::get_collection(collection_id)?;1196 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11971198 // check schema limit1199 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12001201 target_collection.variable_on_chain_schema = schema;1202 target_collection.save()1203 }12041205 #[weight = <SelfWeightOf<T>>::set_collection_limits()]1206 #[transactional]1207 pub fn set_collection_limits(1208 origin,1209 collection_id: u32,1210 new_limits: CollectionLimits<T::BlockNumber>,1211 ) -> DispatchResult {1212 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1213 let mut target_collection = Self::get_collection(collection_id)?;1214 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1215 let old_limits = &target_collection.limits;12161217 // collection bounds1218 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1219 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1220 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1221 Error::<T>::CollectionLimitBoundsExceeded);12221223 // token_limit check prev1224 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1225 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12261227 ensure!(1228 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1229 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1230 Error::<T>::OwnerPermissionsCantBeReverted,1231 );12321233 target_collection.limits = new_limits;12341235 target_collection.save()1236 }1237 }1238}12391240impl<T: Config> Module<T> {1241 pub fn create_item_internal(1242 sender: &T::CrossAccountId,1243 collection: &CollectionHandle<T>,1244 owner: &T::CrossAccountId,1245 data: CreateItemData,1246 ) -> DispatchResult {1247 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1248 Self::validate_create_item_args(collection, &data)?;1249 Self::create_item_no_validation(collection, owner, data)?;12501251 Ok(())1252 }12531254 pub fn transfer_internal(1255 sender: &T::CrossAccountId,1256 recipient: &T::CrossAccountId,1257 target_collection: &CollectionHandle<T>,1258 item_id: TokenId,1259 value: u128,1260 ) -> DispatchResult {1261 target_collection.consume_gas(2000000)?;1262 // Limits check1263 Self::is_correct_transfer(target_collection, recipient)?;12641265 // Transfer permissions check1266 ensure!(1267 Self::is_item_owner(sender, target_collection, item_id)1268 || Self::is_owner_or_admin_permissions(target_collection, sender),1269 Error::<T>::NoPermission1270 );12711272 if target_collection.access == AccessMode::WhiteList {1273 Self::check_white_list(target_collection, sender)?;1274 Self::check_white_list(target_collection, recipient)?;1275 }12761277 match target_collection.mode {1278 CollectionMode::NFT => Self::transfer_nft(1279 target_collection,1280 item_id,1281 sender.clone(),1282 recipient.clone(),1283 )?,1284 CollectionMode::Fungible(_) => {1285 Self::transfer_fungible(target_collection, value, sender, recipient)?1286 }1287 CollectionMode::ReFungible => Self::transfer_refungible(1288 target_collection,1289 item_id,1290 value,1291 sender.clone(),1292 recipient.clone(),1293 )?,1294 _ => (),1295 };12961297 Self::deposit_event(RawEvent::Transfer(1298 target_collection.id,1299 item_id,1300 sender.clone(),1301 recipient.clone(),1302 value,1303 ));13041305 Ok(())1306 }13071308 pub fn approve_internal(1309 sender: &T::CrossAccountId,1310 spender: &T::CrossAccountId,1311 collection: &CollectionHandle<T>,1312 item_id: TokenId,1313 amount: u128,1314 ) -> DispatchResult {1315 collection.consume_gas(2000000)?;1316 Self::token_exists(collection, item_id)?;13171318 // Transfer permissions check1319 let bypasses_limits = collection.limits.owner_can_transfer1320 && Self::is_owner_or_admin_permissions(collection, sender);13211322 let allowance_limit = if bypasses_limits {1323 None1324 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1325 Some(amount)1326 } else {1327 fail!(Error::<T>::NoPermission);1328 };13291330 if collection.access == AccessMode::WhiteList {1331 Self::check_white_list(collection, sender)?;1332 Self::check_white_list(collection, spender)?;1333 }13341335 let allowance: u128 = amount1336 .checked_add(<Allowances<T>>::get(1337 collection.id,1338 (item_id, sender.as_sub(), spender.as_sub()),1339 ))1340 .ok_or(Error::<T>::NumOverflow)?;1341 if let Some(limit) = allowance_limit {1342 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1343 }1344 <Allowances<T>>::insert(1345 collection.id,1346 (item_id, sender.as_sub(), spender.as_sub()),1347 allowance,1348 );13491350 if matches!(collection.mode, CollectionMode::NFT) {1351 // TODO: NFT: only one owner may exist for token in ERC7211352 collection.log(ERC721Events::Approval {1353 owner: *sender.as_eth(),1354 approved: *spender.as_eth(),1355 token_id: item_id.into(),1356 })?;1357 }13581359 if matches!(collection.mode, CollectionMode::Fungible(_)) {1360 // TODO: NFT: only one owner may exist for token in ERC201361 collection.log(ERC20Events::Approval {1362 owner: *sender.as_eth(),1363 spender: *spender.as_eth(),1364 value: allowance.into(),1365 })?;1366 }13671368 Self::deposit_event(RawEvent::Approved(1369 collection.id,1370 item_id,1371 sender.clone(),1372 spender.clone(),1373 allowance,1374 ));1375 Ok(())1376 }13771378 pub fn transfer_from_internal(1379 sender: &T::CrossAccountId,1380 from: &T::CrossAccountId,1381 recipient: &T::CrossAccountId,1382 collection: &CollectionHandle<T>,1383 item_id: TokenId,1384 amount: u128,1385 ) -> DispatchResult {1386 collection.consume_gas(2000000)?;1387 // Check approval1388 let approval: u128 =1389 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13901391 // Limits check1392 Self::is_correct_transfer(collection, recipient)?;13931394 // Transfer permissions check1395 ensure!(1396 approval >= amount1397 || (collection.limits.owner_can_transfer1398 && Self::is_owner_or_admin_permissions(collection, sender)),1399 Error::<T>::NoPermission1400 );14011402 if collection.access == AccessMode::WhiteList {1403 Self::check_white_list(collection, sender)?;1404 Self::check_white_list(collection, recipient)?;1405 }14061407 // Reduce approval by transferred amount or remove if remaining approval drops to 01408 let allowance = approval.saturating_sub(amount);1409 if allowance > 0 {1410 <Allowances<T>>::insert(1411 collection.id,1412 (item_id, from.as_sub(), sender.as_sub()),1413 allowance,1414 );1415 } else {1416 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1417 }14181419 match collection.mode {1420 CollectionMode::NFT => {1421 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1422 }1423 CollectionMode::Fungible(_) => {1424 Self::transfer_fungible(collection, amount, from, recipient)?1425 }1426 CollectionMode::ReFungible => Self::transfer_refungible(1427 collection,1428 item_id,1429 amount,1430 from.clone(),1431 recipient.clone(),1432 )?,1433 _ => (),1434 };14351436 if matches!(collection.mode, CollectionMode::Fungible(_)) {1437 collection.log(ERC20Events::Approval {1438 owner: *from.as_eth(),1439 spender: *sender.as_eth(),1440 value: allowance.into(),1441 })?;1442 }14431444 Ok(())1445 }14461447 pub fn set_variable_meta_data_internal(1448 sender: &T::CrossAccountId,1449 collection: &CollectionHandle<T>,1450 item_id: TokenId,1451 data: Vec<u8>,1452 ) -> DispatchResult {1453 Self::token_exists(collection, item_id)?;14541455 ensure!(1456 CUSTOM_DATA_LIMIT >= data.len() as u32,1457 Error::<T>::TokenVariableDataLimitExceeded1458 );14591460 // Modify permissions check1461 ensure!(1462 Self::is_item_owner(sender, collection, item_id)1463 || Self::is_owner_or_admin_permissions(collection, sender),1464 Error::<T>::NoPermission1465 );14661467 match collection.mode {1468 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1469 CollectionMode::ReFungible => {1470 Self::set_re_fungible_variable_data(collection, item_id, data)?1471 }1472 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1473 _ => fail!(Error::<T>::UnexpectedCollectionType),1474 };14751476 Ok(())1477 }14781479 pub fn create_multiple_items_internal(1480 sender: &T::CrossAccountId,1481 collection: &CollectionHandle<T>,1482 owner: &T::CrossAccountId,1483 items_data: Vec<CreateItemData>,1484 ) -> DispatchResult {1485 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14861487 for data in &items_data {1488 Self::validate_create_item_args(collection, data)?;1489 }1490 for data in &items_data {1491 Self::create_item_no_validation(collection, owner, data.clone())?;1492 }14931494 Ok(())1495 }14961497 pub fn burn_item_internal(1498 sender: &T::CrossAccountId,1499 collection: &CollectionHandle<T>,1500 item_id: TokenId,1501 value: u128,1502 ) -> DispatchResult {1503 ensure!(1504 Self::is_item_owner(sender, collection, item_id)1505 || (collection.limits.owner_can_transfer1506 && Self::is_owner_or_admin_permissions(collection, sender)),1507 Error::<T>::NoPermission1508 );15091510 if collection.access == AccessMode::WhiteList {1511 Self::check_white_list(collection, sender)?;1512 }15131514 match collection.mode {1515 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1516 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1517 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1518 _ => (),1519 };15201521 Ok(())1522 }15231524 pub fn toggle_white_list_internal(1525 sender: &T::CrossAccountId,1526 collection: &CollectionHandle<T>,1527 address: &T::CrossAccountId,1528 whitelisted: bool,1529 ) -> DispatchResult {1530 Self::check_owner_or_admin_permissions(collection, sender)?;15311532 if whitelisted {1533 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1534 } else {1535 <WhiteList<T>>::remove(collection.id, address.as_sub());1536 }15371538 Ok(())1539 }15401541 fn is_correct_transfer(1542 collection: &CollectionHandle<T>,1543 recipient: &T::CrossAccountId,1544 ) -> DispatchResult {1545 let collection_id = collection.id;15461547 // check token limit and account token limit1548 let account_items: u32 =1549 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1550 ensure!(1551 collection.limits.account_token_ownership_limit > account_items,1552 Error::<T>::AccountTokenLimitExceeded1553 );15541555 // preliminary transfer check1556 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15571558 Ok(())1559 }15601561 fn can_create_items_in_collection(1562 collection: &CollectionHandle<T>,1563 sender: &T::CrossAccountId,1564 owner: &T::CrossAccountId,1565 amount: u32,1566 ) -> DispatchResult {1567 let collection_id = collection.id;15681569 // check token limit and account token limit1570 let total_items: u32 = ItemListIndex::get(collection_id)1571 .checked_add(amount)1572 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1573 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1574 as u32)1575 .checked_add(amount)1576 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1577 ensure!(1578 collection.limits.token_limit >= total_items,1579 Error::<T>::CollectionTokenLimitExceeded1580 );1581 ensure!(1582 collection.limits.account_token_ownership_limit >= account_items,1583 Error::<T>::AccountTokenLimitExceeded1584 );15851586 if !Self::is_owner_or_admin_permissions(collection, sender) {1587 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1588 Self::check_white_list(collection, owner)?;1589 Self::check_white_list(collection, sender)?;1590 }15911592 Ok(())1593 }15941595 fn validate_create_item_args(1596 target_collection: &CollectionHandle<T>,1597 data: &CreateItemData,1598 ) -> DispatchResult {1599 match target_collection.mode {1600 CollectionMode::NFT => {1601 if !matches!(data, CreateItemData::NFT(_)) {1602 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1603 }1604 }1605 CollectionMode::Fungible(_) => {1606 if !matches!(data, CreateItemData::Fungible(_)) {1607 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1608 }1609 }1610 CollectionMode::ReFungible => {1611 if let CreateItemData::ReFungible(data) = data {1612 // Check refungibility limits1613 ensure!(1614 data.pieces <= MAX_REFUNGIBLE_PIECES,1615 Error::<T>::WrongRefungiblePieces1616 );1617 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1618 } else {1619 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1620 }1621 }1622 _ => {1623 fail!(Error::<T>::UnexpectedCollectionType);1624 }1625 };16261627 Ok(())1628 }16291630 fn create_item_no_validation(1631 collection: &CollectionHandle<T>,1632 owner: &T::CrossAccountId,1633 data: CreateItemData,1634 ) -> DispatchResult {1635 match data {1636 CreateItemData::NFT(data) => {1637 let item = NftItemType {1638 owner: owner.clone(),1639 const_data: data.const_data.into_inner(),1640 variable_data: data.variable_data.into_inner(),1641 };16421643 Self::add_nft_item(collection, item)?;1644 }1645 CreateItemData::Fungible(data) => {1646 Self::add_fungible_item(collection, owner, data.value)?;1647 }1648 CreateItemData::ReFungible(data) => {1649 let owner_list = vec![Ownership {1650 owner: owner.clone(),1651 fraction: data.pieces,1652 }];16531654 let item = ReFungibleItemType {1655 owner: owner_list,1656 const_data: data.const_data.into_inner(),1657 variable_data: data.variable_data.into_inner(),1658 };16591660 Self::add_refungible_item(collection, item)?;1661 }1662 };16631664 Ok(())1665 }16661667 fn add_fungible_item(1668 collection: &CollectionHandle<T>,1669 owner: &T::CrossAccountId,1670 value: u128,1671 ) -> DispatchResult {1672 let collection_id = collection.id;16731674 // Does new owner already have an account?1675 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16761677 // Mint1678 let item = FungibleItemType {1679 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1680 };1681 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16821683 // Update balance1684 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1685 .checked_add(value)1686 .ok_or(Error::<T>::NumOverflow)?;1687 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16881689 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1690 Ok(())1691 }16921693 fn add_refungible_item(1694 collection: &CollectionHandle<T>,1695 item: ReFungibleItemType<T::CrossAccountId>,1696 ) -> DispatchResult {1697 let collection_id = collection.id;16981699 let current_index = <ItemListIndex>::get(collection_id)1700 .checked_add(1)1701 .ok_or(Error::<T>::NumOverflow)?;1702 let itemcopy = item.clone();17031704 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1705 let item_owner = item.owner.first().expect("only one owner is defined");17061707 let value = item_owner.fraction;1708 let owner = item_owner.owner.clone();17091710 Self::add_token_index(collection_id, current_index, &owner)?;17111712 <ItemListIndex>::insert(collection_id, current_index);1713 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17141715 // Update balance1716 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1717 .checked_add(value)1718 .ok_or(Error::<T>::NumOverflow)?;1719 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17201721 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1722 Ok(())1723 }17241725 fn add_nft_item(1726 collection: &CollectionHandle<T>,1727 item: NftItemType<T::CrossAccountId>,1728 ) -> DispatchResult {1729 let collection_id = collection.id;17301731 let current_index = <ItemListIndex>::get(collection_id)1732 .checked_add(1)1733 .ok_or(Error::<T>::NumOverflow)?;17341735 let item_owner = item.owner.clone();1736 Self::add_token_index(collection_id, current_index, &item.owner)?;17371738 <ItemListIndex>::insert(collection_id, current_index);1739 <NftItemList<T>>::insert(collection_id, current_index, item);17401741 // Update balance1742 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1743 .checked_add(1)1744 .ok_or(Error::<T>::NumOverflow)?;1745 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17461747 collection.log(ERC721Events::Transfer {1748 from: H160::default(),1749 to: *item_owner.as_eth(),1750 token_id: current_index.into(),1751 })?;1752 Self::deposit_event(RawEvent::ItemCreated(1753 collection_id,1754 current_index,1755 item_owner,1756 ));1757 Ok(())1758 }17591760 fn burn_refungible_item(1761 collection: &CollectionHandle<T>,1762 item_id: TokenId,1763 owner: &T::CrossAccountId,1764 ) -> DispatchResult {1765 let collection_id = collection.id;17661767 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1768 .ok_or(Error::<T>::TokenNotFound)?;1769 let rft_balance = token1770 .owner1771 .iter()1772 .find(|&i| i.owner == *owner)1773 .ok_or(Error::<T>::TokenNotFound)?;1774 Self::remove_token_index(collection_id, item_id, owner)?;17751776 // update balance1777 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1778 .checked_sub(rft_balance.fraction)1779 .ok_or(Error::<T>::NumOverflow)?;1780 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17811782 // Re-create owners list with sender removed1783 let index = token1784 .owner1785 .iter()1786 .position(|i| i.owner == *owner)1787 .expect("owned item is exists");1788 token.owner.remove(index);1789 let owner_count = token.owner.len();17901791 // Burn the token completely if this was the last (only) owner1792 if owner_count == 0 {1793 <ReFungibleItemList<T>>::remove(collection_id, item_id);1794 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1795 } else {1796 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1797 }17981799 Ok(())1800 }18011802 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1803 let collection_id = collection.id;18041805 let item =1806 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1807 Self::remove_token_index(collection_id, item_id, &item.owner)?;18081809 // update balance1810 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1811 .checked_sub(1)1812 .ok_or(Error::<T>::NumOverflow)?;1813 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1814 <NftItemList<T>>::remove(collection_id, item_id);1815 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18161817 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1818 Ok(())1819 }18201821 fn burn_fungible_item(1822 owner: &T::CrossAccountId,1823 collection: &CollectionHandle<T>,1824 value: u128,1825 ) -> DispatchResult {1826 let collection_id = collection.id;18271828 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1829 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18301831 // update balance1832 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1833 .checked_sub(value)1834 .ok_or(Error::<T>::NumOverflow)?;1835 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18361837 if balance.value - value > 0 {1838 balance.value -= value;1839 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1840 } else {1841 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1842 }18431844 collection.log(ERC20Events::Transfer {1845 from: *owner.as_eth(),1846 to: H160::default(),1847 value: value.into(),1848 })?;1849 Ok(())1850 }18511852 pub fn get_collection(1853 collection_id: CollectionId,1854 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1855 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1856 }18571858 fn check_owner_permissions(1859 target_collection: &CollectionHandle<T>,1860 subject: &T::AccountId,1861 ) -> DispatchResult {1862 ensure!(1863 *subject == target_collection.owner,1864 Error::<T>::NoPermission1865 );18661867 Ok(())1868 }18691870 fn is_owner_or_admin_permissions(1871 collection: &CollectionHandle<T>,1872 subject: &T::CrossAccountId,1873 ) -> bool {1874 *subject.as_sub() == collection.owner1875 || <AdminList<T>>::get(collection.id).contains(subject)1876 }18771878 fn check_owner_or_admin_permissions(1879 collection: &CollectionHandle<T>,1880 subject: &T::CrossAccountId,1881 ) -> DispatchResult {1882 ensure!(1883 Self::is_owner_or_admin_permissions(collection, subject),1884 Error::<T>::NoPermission1885 );18861887 Ok(())1888 }18891890 fn owned_amount(1891 subject: &T::CrossAccountId,1892 target_collection: &CollectionHandle<T>,1893 item_id: TokenId,1894 ) -> Option<u128> {1895 let collection_id = target_collection.id;18961897 match target_collection.mode {1898 CollectionMode::NFT => {1899 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1900 }1901 CollectionMode::Fungible(_) => {1902 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1903 }1904 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1905 .owner1906 .iter()1907 .find(|i| i.owner == *subject)1908 .map(|i| i.fraction),1909 CollectionMode::Invalid => None,1910 }1911 }19121913 fn is_item_owner(1914 subject: &T::CrossAccountId,1915 target_collection: &CollectionHandle<T>,1916 item_id: TokenId,1917 ) -> bool {1918 match target_collection.mode {1919 CollectionMode::Fungible(_) => true,1920 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1921 }1922 }19231924 fn check_white_list(1925 collection: &CollectionHandle<T>,1926 address: &T::CrossAccountId,1927 ) -> DispatchResult {1928 let collection_id = collection.id;19291930 let mes = Error::<T>::AddresNotInWhiteList;1931 ensure!(1932 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1933 mes1934 );19351936 Ok(())1937 }19381939 /// Check if token exists. In case of Fungible, check if there is an entry for1940 /// the owner in fungible balances double map1941 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1942 let collection_id = target_collection.id;1943 let exists = match target_collection.mode {1944 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1945 CollectionMode::Fungible(_) => true,1946 CollectionMode::ReFungible => {1947 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1948 }1949 _ => false,1950 };19511952 ensure!(exists, Error::<T>::TokenNotFound);1953 Ok(())1954 }19551956 fn transfer_fungible(1957 collection: &CollectionHandle<T>,1958 value: u128,1959 owner: &T::CrossAccountId,1960 recipient: &T::CrossAccountId,1961 ) -> DispatchResult {1962 let collection_id = collection.id;19631964 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1965 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19661967 // Send balance to recipient (updates balanceOf of recipient)1968 Self::add_fungible_item(collection, recipient, value)?;19691970 // update balanceOf of sender1971 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19721973 // Reduce or remove sender1974 if balance.value == value {1975 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1976 } else {1977 balance.value -= value;1978 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1979 }19801981 collection.log(ERC20Events::Transfer {1982 from: *owner.as_eth(),1983 to: *recipient.as_eth(),1984 value: value.into(),1985 })?;1986 Self::deposit_event(RawEvent::Transfer(1987 collection.id,1988 1,1989 owner.clone(),1990 recipient.clone(),1991 value,1992 ));19931994 Ok(())1995 }19961997 fn transfer_refungible(1998 collection: &CollectionHandle<T>,1999 item_id: TokenId,2000 value: u128,2001 owner: T::CrossAccountId,2002 new_owner: T::CrossAccountId,2003 ) -> DispatchResult {2004 let collection_id = collection.id;2005 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2006 .ok_or(Error::<T>::TokenNotFound)?;20072008 let item = full_item2009 .owner2010 .iter()2011 .find(|i| i.owner == owner)2012 .ok_or(Error::<T>::TokenNotFound)?;2013 let amount = item.fraction;20142015 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20162017 // update balance2018 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2019 .checked_sub(value)2020 .ok_or(Error::<T>::NumOverflow)?;2021 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20222023 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2024 .checked_add(value)2025 .ok_or(Error::<T>::NumOverflow)?;2026 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20272028 let old_owner = item.owner.clone();2029 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20302031 let mut new_full_item = full_item.clone();2032 // transfer2033 if amount == value && !new_owner_has_account {2034 // change owner2035 // new owner do not have account2036 new_full_item2037 .owner2038 .iter_mut()2039 .find(|i| i.owner == owner)2040 .expect("old owner does present in refungible")2041 .owner = new_owner.clone();2042 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20432044 // update index collection2045 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2046 } else {2047 new_full_item2048 .owner2049 .iter_mut()2050 .find(|i| i.owner == owner)2051 .expect("old owner does present in refungible")2052 .fraction -= value;20532054 // separate amount2055 if new_owner_has_account {2056 // new owner has account2057 new_full_item2058 .owner2059 .iter_mut()2060 .find(|i| i.owner == new_owner)2061 .expect("new owner has account")2062 .fraction += value;2063 } else {2064 // new owner do not have account2065 new_full_item.owner.push(Ownership {2066 owner: new_owner.clone(),2067 fraction: value,2068 });2069 Self::add_token_index(collection_id, item_id, &new_owner)?;2070 }20712072 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2073 }20742075 Self::deposit_event(RawEvent::Transfer(2076 collection.id,2077 item_id,2078 owner,2079 new_owner,2080 amount,2081 ));20822083 Ok(())2084 }20852086 fn transfer_nft(2087 collection: &CollectionHandle<T>,2088 item_id: TokenId,2089 sender: T::CrossAccountId,2090 new_owner: T::CrossAccountId,2091 ) -> DispatchResult {2092 let collection_id = collection.id;2093 let mut item =2094 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20952096 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20972098 // update balance2099 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2100 .checked_sub(1)2101 .ok_or(Error::<T>::NumOverflow)?;2102 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21032104 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2105 .checked_add(1)2106 .ok_or(Error::<T>::NumOverflow)?;2107 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21082109 // change owner2110 let old_owner = item.owner.clone();2111 item.owner = new_owner.clone();2112 <NftItemList<T>>::insert(collection_id, item_id, item);21132114 // update index collection2115 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21162117 collection.log(ERC721Events::Transfer {2118 from: *sender.as_eth(),2119 to: *new_owner.as_eth(),2120 token_id: item_id.into(),2121 })?;2122 Self::deposit_event(RawEvent::Transfer(2123 collection.id,2124 item_id,2125 sender,2126 new_owner,2127 1,2128 ));21292130 Ok(())2131 }21322133 fn set_re_fungible_variable_data(2134 collection: &CollectionHandle<T>,2135 item_id: TokenId,2136 data: Vec<u8>,2137 ) -> DispatchResult {2138 let collection_id = collection.id;2139 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2140 .ok_or(Error::<T>::TokenNotFound)?;21412142 item.variable_data = data;21432144 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21452146 Ok(())2147 }21482149 fn set_nft_variable_data(2150 collection: &CollectionHandle<T>,2151 item_id: TokenId,2152 data: Vec<u8>,2153 ) -> DispatchResult {2154 let collection_id = collection.id;2155 let mut item =2156 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21572158 item.variable_data = data;21592160 <NftItemList<T>>::insert(collection_id, item_id, item);21612162 Ok(())2163 }21642165 #[allow(dead_code)]2166 fn init_collection(item: &Collection<T>) {2167 // check params2168 assert!(2169 item.decimal_points <= MAX_DECIMAL_POINTS,2170 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2171 );2172 assert!(2173 item.name.len() <= 64,2174 "Collection name can not be longer than 63 char"2175 );2176 assert!(2177 item.name.len() <= 256,2178 "Collection description can not be longer than 255 char"2179 );2180 assert!(2181 item.token_prefix.len() <= 16,2182 "Token prefix can not be longer than 15 char"2183 );21842185 // Generate next collection ID2186 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21872188 CreatedCollectionCount::put(next_id);2189 }21902191 #[allow(dead_code)]2192 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2193 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21942195 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21962197 <ItemListIndex>::insert(collection_id, current_index);21982199 // Update balance2200 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2201 .checked_add(1)2202 .unwrap();2203 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2204 }22052206 #[allow(dead_code)]2207 fn init_fungible_token(2208 collection_id: CollectionId,2209 owner: &T::CrossAccountId,2210 item: &FungibleItemType,2211 ) {2212 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22132214 Self::add_token_index(collection_id, current_index, owner).unwrap();22152216 <ItemListIndex>::insert(collection_id, current_index);22172218 // Update balance2219 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2220 .checked_add(item.value)2221 .unwrap();2222 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2223 }22242225 #[allow(dead_code)]2226 fn init_refungible_token(2227 collection_id: CollectionId,2228 item: &ReFungibleItemType<T::CrossAccountId>,2229 ) {2230 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22312232 let value = item.owner.first().unwrap().fraction;2233 let owner = item.owner.first().unwrap().owner.clone();22342235 Self::add_token_index(collection_id, current_index, &owner).unwrap();22362237 <ItemListIndex>::insert(collection_id, current_index);22382239 // Update balance2240 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2241 .checked_add(value)2242 .unwrap();2243 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2244 }22452246 fn add_token_index(2247 collection_id: CollectionId,2248 item_index: TokenId,2249 owner: &T::CrossAccountId,2250 ) -> DispatchResult {2251 // add to account limit2252 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2253 // bound Owned tokens by a single address2254 let count = <AccountItemCount<T>>::get(owner.as_sub());2255 ensure!(2256 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2257 Error::<T>::AddressOwnershipLimitExceeded2258 );22592260 <AccountItemCount<T>>::insert(2261 owner.as_sub(),2262 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2263 );2264 } else {2265 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2266 }22672268 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2269 if list_exists {2270 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2271 let item_contains = list.contains(&item_index.clone());22722273 if !item_contains {2274 list.push(item_index);2275 }22762277 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2278 } else {2279 let itm = vec![item_index];2280 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2281 }22822283 Ok(())2284 }22852286 fn remove_token_index(2287 collection_id: CollectionId,2288 item_index: TokenId,2289 owner: &T::CrossAccountId,2290 ) -> DispatchResult {2291 // update counter2292 <AccountItemCount<T>>::insert(2293 owner.as_sub(),2294 <AccountItemCount<T>>::get(owner.as_sub())2295 .checked_sub(1)2296 .ok_or(Error::<T>::NumOverflow)?,2297 );22982299 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2300 if list_exists {2301 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2302 let item_contains = list.contains(&item_index.clone());23032304 if item_contains {2305 list.retain(|&item| item != item_index);2306 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2307 }2308 }23092310 Ok(())2311 }23122313 fn move_token_index(2314 collection_id: CollectionId,2315 item_index: TokenId,2316 old_owner: &T::CrossAccountId,2317 new_owner: &T::CrossAccountId,2318 ) -> DispatchResult {2319 Self::remove_token_index(collection_id, item_index, old_owner)?;2320 Self::add_token_index(collection_id, item_index, new_owner)?;23212322 Ok(())2323 }2324}23252326sp_api::decl_runtime_apis! {2327 pub trait NftApi {2328 /// Used for ethereum integration2329 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2330 }2331}pallets/nft/src/weights.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/weights.rs
@@ -0,0 +1,362 @@
+// Template adopted from https://github.com/paritytech/substrate/blob/master/.maintain/frame-weight-template.hbs
+
+//! Autogenerated weights for pallet_nft
+//!
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0
+//! DATE: 2021-08-30, STEPS: `[50, ]`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]`
+//! EXECUTION: None, WASM-EXECUTION: Compiled, CHAIN: None, DB CACHE: 128
+
+// Executed Command:
+// target/release/nft
+// benchmark
+// --pallet
+// pallet-nft
+// --wasm-execution
+// compiled
+// --extrinsic
+// *
+// --template
+// .maintain/frame-weight-template.hbs
+// --steps=50
+// --repeat=20
+// --output=./pallets/nft/src/weights.rs
+
+
+#![cfg_attr(rustfmt, rustfmt_skip)]
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
+use sp_std::marker::PhantomData;
+
+/// Weight functions needed for pallet_nft.
+pub trait WeightInfo {
+ fn create_collection() -> Weight;
+ fn destroy_collection() -> Weight;
+ fn add_to_white_list() -> Weight;
+ fn remove_from_white_list() -> Weight;
+ fn set_public_access_mode() -> Weight;
+ fn set_mint_permission() -> Weight;
+ fn change_collection_owner() -> Weight;
+ fn add_collection_admin() -> Weight;
+ fn remove_collection_admin() -> Weight;
+ fn set_collection_sponsor() -> Weight;
+ fn confirm_sponsorship() -> Weight;
+ fn remove_collection_sponsor() -> Weight;
+ fn create_item_nft(b: u32, ) -> Weight;
+ fn create_item_fungible() -> Weight;
+ fn create_item_refungible(b: u32, ) -> Weight;
+ fn burn_item_nft() -> Weight;
+ fn transfer_nft() -> Weight;
+ fn transfer_fungible() -> Weight;
+ fn transfer_refungible() -> Weight;
+ fn approve_nft() -> Weight;
+ fn transfer_from_nft() -> Weight;
+ fn transfer_from_fungible() -> Weight;
+ fn transfer_from_refungible() -> Weight;
+ fn set_offchain_schema(b: u32, ) -> Weight;
+ fn set_const_on_chain_schema(b: u32, ) -> Weight;
+ fn set_variable_on_chain_schema(b: u32, ) -> Weight;
+ fn set_variable_meta_data_nft(b: u32, ) -> Weight;
+ fn set_schema_version() -> Weight;
+ fn set_collection_limits() -> Weight;
+}
+
+/// Weights for pallet_nft using the Substrate node and recommended hardware.
+pub struct SubstrateWeight<T>(PhantomData<T>);
+impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
+ fn create_collection() -> Weight {
+ (25_851_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(8 as Weight))
+ .saturating_add(T::DbWeight::get().writes(6 as Weight))
+ }
+ fn destroy_collection() -> Weight {
+ (28_737_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(2 as Weight))
+ .saturating_add(T::DbWeight::get().writes(4 as Weight))
+ }
+ fn add_to_white_list() -> Weight {
+ (6_237_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn remove_from_white_list() -> Weight {
+ (6_252_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_public_access_mode() -> Weight {
+ (6_691_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_mint_permission() -> Weight {
+ (6_630_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn change_collection_owner() -> Weight {
+ (6_521_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn add_collection_admin() -> Weight {
+ (8_057_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(2 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn remove_collection_admin() -> Weight {
+ (8_307_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(2 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_collection_sponsor() -> Weight {
+ (6_484_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn confirm_sponsorship() -> Weight {
+ (6_530_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn remove_collection_sponsor() -> Weight {
+ (6_733_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn create_item_nft(_b: u32, ) -> Weight {
+ (167_909_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(11 as Weight))
+ .saturating_add(T::DbWeight::get().writes(8 as Weight))
+ }
+ fn create_item_fungible() -> Weight {
+ (22_331_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(9 as Weight))
+ .saturating_add(T::DbWeight::get().writes(4 as Weight))
+ }
+ fn create_item_refungible(_b: u32, ) -> Weight {
+ (26_293_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(9 as Weight))
+ .saturating_add(T::DbWeight::get().writes(7 as Weight))
+ }
+ fn burn_item_nft() -> Weight {
+ (32_237_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(9 as Weight))
+ .saturating_add(T::DbWeight::get().writes(7 as Weight))
+ }
+ fn transfer_nft() -> Weight {
+ (192_578_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(14 as Weight))
+ .saturating_add(T::DbWeight::get().writes(10 as Weight))
+ }
+ fn transfer_fungible() -> Weight {
+ (170_749_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(11 as Weight))
+ .saturating_add(T::DbWeight::get().writes(7 as Weight))
+ }
+ fn transfer_refungible() -> Weight {
+ (35_949_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(10 as Weight))
+ .saturating_add(T::DbWeight::get().writes(7 as Weight))
+ }
+ fn approve_nft() -> Weight {
+ (169_825_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(9 as Weight))
+ .saturating_add(T::DbWeight::get().writes(4 as Weight))
+ }
+ fn transfer_from_nft() -> Weight {
+ (197_912_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(15 as Weight))
+ .saturating_add(T::DbWeight::get().writes(11 as Weight))
+ }
+ fn transfer_from_fungible() -> Weight {
+ (183_789_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(12 as Weight))
+ .saturating_add(T::DbWeight::get().writes(8 as Weight))
+ }
+ fn transfer_from_refungible() -> Weight {
+ (37_149_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(11 as Weight))
+ .saturating_add(T::DbWeight::get().writes(8 as Weight))
+ }
+ fn set_offchain_schema(_b: u32, ) -> Weight {
+ (6_435_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+ (6_646_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+ (6_542_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+ (14_697_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(2 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_schema_version() -> Weight {
+ (6_566_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+ fn set_collection_limits() -> Weight {
+ (6_349_000 as Weight)
+ .saturating_add(T::DbWeight::get().reads(1 as Weight))
+ .saturating_add(T::DbWeight::get().writes(1 as Weight))
+ }
+}
+
+// For backwards compatibility and tests
+impl WeightInfo for () {
+ fn create_collection() -> Weight {
+ (25_851_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(8 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(6 as Weight))
+ }
+ fn destroy_collection() -> Weight {
+ (28_737_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(2 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(4 as Weight))
+ }
+ fn add_to_white_list() -> Weight {
+ (6_237_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn remove_from_white_list() -> Weight {
+ (6_252_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_public_access_mode() -> Weight {
+ (6_691_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_mint_permission() -> Weight {
+ (6_630_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn change_collection_owner() -> Weight {
+ (6_521_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn add_collection_admin() -> Weight {
+ (8_057_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(2 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn remove_collection_admin() -> Weight {
+ (8_307_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(2 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_collection_sponsor() -> Weight {
+ (6_484_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn confirm_sponsorship() -> Weight {
+ (6_530_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn remove_collection_sponsor() -> Weight {
+ (6_733_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn create_item_nft(_b: u32, ) -> Weight {
+ (167_909_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(11 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(8 as Weight))
+ }
+ fn create_item_fungible() -> Weight {
+ (22_331_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(9 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(4 as Weight))
+ }
+ fn create_item_refungible(_b: u32, ) -> Weight {
+ (26_293_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(9 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(7 as Weight))
+ }
+ fn burn_item_nft() -> Weight {
+ (32_237_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(9 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(7 as Weight))
+ }
+ fn transfer_nft() -> Weight {
+ (192_578_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(14 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(10 as Weight))
+ }
+ fn transfer_fungible() -> Weight {
+ (170_749_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(11 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(7 as Weight))
+ }
+ fn transfer_refungible() -> Weight {
+ (35_949_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(10 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(7 as Weight))
+ }
+ fn approve_nft() -> Weight {
+ (169_825_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(9 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(4 as Weight))
+ }
+ fn transfer_from_nft() -> Weight {
+ (197_912_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(15 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(11 as Weight))
+ }
+ fn transfer_from_fungible() -> Weight {
+ (183_789_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(12 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(8 as Weight))
+ }
+ fn transfer_from_refungible() -> Weight {
+ (37_149_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(11 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(8 as Weight))
+ }
+ fn set_offchain_schema(_b: u32, ) -> Weight {
+ (6_435_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_const_on_chain_schema(_b: u32, ) -> Weight {
+ (6_646_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_on_chain_schema(_b: u32, ) -> Weight {
+ (6_542_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_variable_meta_data_nft(_b: u32, ) -> Weight {
+ (14_697_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(2 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_schema_version() -> Weight {
+ (6_566_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+ fn set_collection_limits() -> Weight {
+ (6_349_000 as Weight)
+ .saturating_add(RocksDbWeight::get().reads(1 as Weight))
+ .saturating_add(RocksDbWeight::get().writes(1 as Weight))
+ }
+}
\ No newline at end of file
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -55,6 +55,10 @@
pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
+pub const MAX_COLLECTION_NAME_LENGTH: usize = 64;
+pub const MAX_COLLECTION_DESCRIPTION_LENGTH: usize = 256;
+pub const MAX_TOKEN_PREFIX_LENGTH: usize = 16;
+
/// How much items can be created per single
/// create_many call
pub const MAX_ITEMS_PER_BATCH: u32 = 200;
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -119,8 +119,6 @@
/// Digest item type.
pub type DigestItem = generic::DigestItem<Hash>;
-mod nft_weights;
-
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
@@ -695,7 +693,7 @@
/// Used for the pallet nft in `./nft.rs`
impl pallet_nft::Config for Runtime {
type Event = Event;
- type WeightInfo = nft_weights::WeightInfo;
+ type WeightInfo = pallet_nft::weights::SubstrateWeight<Self>;
type EvmBackwardsAddressMapping = pallet_nft::MapBackwardsAddressTruncated;
type EvmAddressMapping = HashedAddressMapping<Self::Hashing>;
runtime/src/nft_weights.rsdiffbeforeafterboth--- a/runtime/src/nft_weights.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-//
-// This file is subject to the terms and conditions defined in
-// file 'LICENSE', which is part of this source code package.
-//
-
-use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};
-
-pub struct WeightInfo;
-impl pallet_nft::WeightInfo for WeightInfo {
- fn create_collection() -> Weight {
- 70_000_000_u64
- .saturating_add(DbWeight::get().reads(7_u64))
- .saturating_add(DbWeight::get().writes(5_u64))
- }
- fn destroy_collection() -> Weight {
- 90_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(5_u64))
- }
- fn add_to_white_list() -> Weight {
- 30_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_from_white_list() -> Weight {
- 35_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_public_access_mode() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_mint_permission() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn change_collection_owner() -> Weight {
- 27_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn add_collection_admin() -> Weight {
- 32_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_collection_admin() -> Weight {
- 50_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_collection_sponsor() -> Weight {
- 32_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn confirm_sponsorship() -> Weight {
- 22_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn remove_collection_sponsor() -> Weight {
- 24_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn create_item(s: usize) -> Weight {
- 130_000_000_u64
- .saturating_add(2135_u64.saturating_mul(s as Weight).saturating_mul(500_u64)) // 500 is temparary multiplier, fee for storage
- .saturating_add(DbWeight::get().reads(10_u64))
- .saturating_add(DbWeight::get().writes(8_u64))
- }
- fn burn_item() -> Weight {
- 170_000_000_u64
- .saturating_add(DbWeight::get().reads(9_u64))
- .saturating_add(DbWeight::get().writes(7_u64))
- }
- fn transfer() -> Weight {
- 125_000_000_u64
- .saturating_add(DbWeight::get().reads(7_u64))
- .saturating_add(DbWeight::get().writes(7_u64))
- }
- fn approve() -> Weight {
- 45_000_000_u64
- .saturating_add(DbWeight::get().reads(3_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn transfer_from() -> Weight {
- 150_000_000_u64
- .saturating_add(DbWeight::get().reads(9_u64))
- .saturating_add(DbWeight::get().writes(8_u64))
- }
- fn set_offchain_schema() -> Weight {
- 33_000_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_const_on_chain_schema() -> Weight {
- 11_100_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_variable_on_chain_schema() -> Weight {
- 11_100_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_variable_meta_data() -> Weight {
- 17_500_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn enable_contract_sponsoring() -> Weight {
- 13_000_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_schema_version() -> Weight {
- 8_500_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
- fn set_contract_sponsoring_rate_limit() -> Weight {
- 3_500_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn set_variable_meta_data_sponsoring_rate_limit() -> Weight {
- 3_500_000_u64
- .saturating_add(DbWeight::get().reads(1_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn toggle_contract_white_list() -> Weight {
- 3_000_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn add_to_contract_white_list() -> Weight {
- 3_000_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn remove_from_contract_white_list() -> Weight {
- 3_200_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(2_u64))
- }
- fn set_collection_limits() -> Weight {
- 8_900_000_u64
- .saturating_add(DbWeight::get().reads(2_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
-}