difftreelog
Weights added
in: master
3 files changed
pallets/nft/src/benchmarking.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/nft/src/benchmarking.rs
@@ -0,0 +1,279 @@
+#[cfg(feature = "runtime-benchmarks")]
+// mod benchmarking {
+ use super::*;
+ use sp_std::prelude::*;
+ use frame_system::RawOrigin;
+ // use frame_support::{ensure, traits::OnFinalize};
+ use frame_benchmarking::{benchmarks, account, whitelisted_caller}; // , TrackedStorageKey,
+ use crate::Module as Nft;
+
+ const SEED: u32 = 1;
+
+ 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 mode: CollectionMode = CollectionMode::NFT(2000);
+ let caller: T::AccountId = account("caller", 0, SEED);
+ }: create_collection(RawOrigin::Signed(caller.clone()), col_name1.clone(), col_desc1.clone(), token_prefix1.clone(), mode)
+ verify {
+ assert_eq!(Nft::<T>::collection(2).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(2000);
+ 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())?;
+ }: destroy_collection(RawOrigin::Signed(caller.clone()), 2)
+
+ 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(2000);
+ let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ 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)
+
+ 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(2000);
+ let caller: T::AccountId = T::AccountId::from(whitelisted_caller());
+ 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)
+
+ 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(2000);
+ 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)
+
+ 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(2000);
+ 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)
+
+ 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(2000);
+ 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 new_owner: T::AccountId = account("admin", 0, SEED);
+ }: change_collection_owner(RawOrigin::Signed(caller.clone()), 2, 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(2000);
+ 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 new_admin: T::AccountId = account("admin", 0, SEED);
+ }: add_collection_admin(RawOrigin::Signed(caller.clone()), 2, 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(2000);
+ 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 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)
+
+ 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(2000);
+ 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())
+
+ 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(2000);
+ 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)
+
+ 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(2000);
+ 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)
+
+ // 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(2000);
+ 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())?;
+
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, [1, 2, 3].to_vec(), caller.clone())
+
+ // 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())?;
+
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, [].to_vec(), caller.clone())
+
+ // 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, 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())?;
+
+ }: create_item(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec(), caller.clone())
+
+ 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(2000);
+ 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>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1, 2, 3].to_vec(), caller.clone())?;
+
+ }: burn_item(RawOrigin::Signed(caller.clone()), 2, 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(2000);
+ 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1, 2, 3].to_vec(), caller.clone())?;
+
+ }: transfer(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 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 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [].to_vec(), caller.clone())?;
+
+ }: 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,3);
+ 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+
+ }: 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,3);
+ 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+
+ }: approve(RawOrigin::Signed(caller.clone()), recipient.clone(), 2, 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(300);
+ 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ 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)
+
+ // 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 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [].to_vec(), caller.clone())?;
+ 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)
+
+ // 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,3);
+ 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())?;
+ Nft::<T>::create_item(RawOrigin::Signed(caller.clone()).into(), 2, [1,2,3].to_vec(), caller.clone())?;
+ 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)
+
+ 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,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_offchain_schema(RawOrigin::Signed(caller.clone()), 2, [1,2,3].to_vec())
+ }
\ No newline at end of file
pallets/nft/src/lib.rsdiffbeforeafterboth1#![cfg_attr(not(feature = "std"), no_std)]23#[cfg(feature = "std")]4pub use std::*;56#[cfg(feature = "std")]7pub use serde::*;89use codec::{Decode, Encode};10pub use frame_support::{11 construct_runtime, decl_event, decl_module, decl_storage,12 dispatch::DispatchResult,13 ensure, parameter_types,14 traits::{15 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,16 Randomness, WithdrawReason,17 },18 weights::{19 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},20 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,21 WeightToFeePolynomial,22 },23 IsSubType, StorageValue,24};25// use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight};2627use frame_system::{self as system, ensure_signed, ensure_root};28use sp_runtime::sp_std::prelude::Vec;29use sp_runtime::{30 traits::{31 DispatchInfoOf, Dispatchable, PostDispatchInfoOf, SaturatedConversion, Saturating,32 SignedExtension, Zero,33 },34 transaction_validity::{35 InvalidTransaction, TransactionPriority, TransactionValidity, TransactionValidityError,36 ValidTransaction,37 },38 FixedPointOperand, FixedU128,39};4041#[cfg(test)]42mod mock;4344#[cfg(test)]45mod tests;4647// Structs48// #region4950#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]51#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]52pub enum CollectionMode {53 Invalid,54 // custom data size55 NFT(u32),56 // decimal points57 Fungible(u32),58 // custom data size and decimal points59 ReFungible(u32, u32),60}6162impl Into<u8> for CollectionMode {63 fn into(self) -> u8 {64 match self {65 CollectionMode::Invalid => 0,66 CollectionMode::NFT(_) => 1,67 CollectionMode::Fungible(_) => 2,68 CollectionMode::ReFungible(_, _) => 3,69 }70 }71}7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum AccessMode {76 Normal,77 WhiteList,78}79impl Default for AccessMode {80 fn default() -> Self {81 Self::Normal82 }83}8485impl Default for CollectionMode {86 fn default() -> Self {87 Self::Invalid88 }89}9091#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]92#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]93pub struct Ownership<AccountId> {94 pub owner: AccountId,95 pub fraction: u128,96}9798#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]99#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]100pub struct CollectionType<AccountId> {101 pub owner: AccountId,102 pub mode: CollectionMode,103 pub access: AccessMode,104 pub decimal_points: u32,105 pub name: Vec<u16>, // 64 include null escape char106 pub description: Vec<u16>, // 256 include null escape char107 pub token_prefix: Vec<u8>, // 16 include null escape char108 pub custom_data_size: u32,109 pub mint_mode: bool,110 pub offchain_schema: Vec<u8>,111 pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender112 pub unconfirmed_sponsor: AccountId, // Sponsor address that has not yet confirmed sponsorship113}114115#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]116#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]117pub struct CollectionAdminsType<AccountId> {118 pub admin: AccountId,119 pub collection_id: u64,120}121122#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]123#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]124pub struct NftItemType<AccountId> {125 pub collection: u64,126 pub owner: AccountId,127 pub data: Vec<u8>,128}129130#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]131#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]132pub struct FungibleItemType<AccountId> {133 pub collection: u64,134 pub owner: AccountId,135 pub value: u128,136}137138#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]139#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]140pub struct ReFungibleItemType<AccountId> {141 pub collection: u64,142 pub owner: Vec<Ownership<AccountId>>,143 pub data: Vec<u8>,144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct ApprovePermissions<AccountId> {149 pub approved: AccountId,150 pub amount: u64,151}152153#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]154#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]155pub struct VestingItem<AccountId, Moment> {156 pub sender: AccountId,157 pub recipient: AccountId,158 pub collection_id: u64,159 pub item_id: u64,160 pub amount: u64,161 pub vesting_date: Moment,162}163164#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]165#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]166pub struct BasketItem<AccountId, BlockNumber> {167 pub address: AccountId,168 pub start_block: BlockNumber,169}170171#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]172#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]173pub struct ChainLimits {174 pub collection_numbers_limit: u64,175 pub account_token_ownership_limit: u64,176 pub collections_admins_limit: u64,177 pub custom_data_limit: u32,178179 // Timeouts for item types in passed blocks180 pub nft_sponsor_transfer_timeout: u32,181 pub fungible_sponsor_transfer_timeout: u32,182 pub refungible_sponsor_transfer_timeout: u32,183}184185pub trait Trait: system::Trait {186 type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;187}188189#[cfg(feature = "runtime-benchmarks")]190mod benchmarking;191192// #endregion193194decl_storage! {195 trait Store for Module<T: Trait> as Nft {196197 // Private members198 NextCollectionID: u64;199 CreatedCollectionCount: u64;200 ChainVersion: u64;201 ItemListIndex: map hasher(blake2_128_concat) u64 => u64;202203 // Chain limits struct204 pub ChainLimit get(fn chain_limit) config(): ChainLimits;205206 // Bound counters207 CollectionCount: u64;208 pub AccountItemCount get(fn account_item_count): map hasher(identity) T::AccountId => u64;209210 // Basic collections211 pub Collection get(fn collection) config(): map hasher(identity) u64 => CollectionType<T::AccountId>;212 pub AdminList get(fn admin_list_collection): map hasher(identity) u64 => Vec<T::AccountId>;213 pub WhiteList get(fn white_list): map hasher(identity) u64 => Vec<T::AccountId>;214215 /// Balance owner per collection map216 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => u64;217218 /// second parameter: item id + owner account id219 pub ApprovedList get(fn approved): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) (u64, T::AccountId) => Vec<ApprovePermissions<T::AccountId>>;220221 /// Item collections222 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => NftItemType<T::AccountId>;223 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => FungibleItemType<T::AccountId>;224 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => ReFungibleItemType<T::AccountId>;225226 /// Index list227 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) T::AccountId => Vec<u64>;228229 /// Tokens transfer baskets230 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;231 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => Vec<BasketItem<T::AccountId, T::BlockNumber>>;232 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) u64, hasher(blake2_128_concat) u64 => T::BlockNumber;233234 // Sponsorship235 pub ContractSponsor get(fn contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;236 pub UnconfirmedContractSponsor get(fn unconfirmed_contract_sponsor): map hasher(identity) T::AccountId => T::AccountId;237 }238 add_extra_genesis {239 build(|config: &GenesisConfig<T>| {240 // Modification of storage241 for (_num, _c) in &config.collection {242 <Module<T>>::init_collection(_c);243 }244245 for (_num, _q, _i) in &config.nft_item_id {246 <Module<T>>::init_nft_token(_i);247 }248249 for (_num, _q, _i) in &config.fungible_item_id {250 <Module<T>>::init_fungible_token(_i);251 }252253 for (_num, _q, _i) in &config.refungible_item_id {254 <Module<T>>::init_refungible_token(_i);255 }256 })257 }258}259260decl_event!(261 pub enum Event<T>262 where263 AccountId = <T as system::Trait>::AccountId,264 {265 /// New collection was created266 /// 267 /// # Arguments268 /// 269 /// * collection_id: Globally unique identifier of newly created collection.270 /// 271 /// * mode: [CollectionMode] converted into u8.272 /// 273 /// * account_id: Collection owner.274 Created(u64, u8, AccountId),275276 /// New item was created.277 /// 278 /// # Arguments279 /// 280 /// * collection_id: Id of the collection where item was created.281 /// 282 /// * item_id: Id of an item. Unique within the collection.283 ItemCreated(u64, u64),284285 /// Collection item was burned.286 /// 287 /// # Arguments288 /// 289 /// collection_id.290 /// 291 /// item_id: Identifier of burned NFT.292 ItemDestroyed(u64, u64),293 }294);295296decl_module! {297 pub struct Module<T: Trait> for enum Call where origin: T::Origin {298299 fn deposit_event() = default;300301 fn on_initialize(now: T::BlockNumber) -> Weight {302303 if ChainVersion::get() < 2304 {305 let value = NextCollectionID::get();306 CreatedCollectionCount::put(value);307 ChainVersion::put(2);308 }309310 0311 }312313 /// 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.314 /// 315 /// # Permissions316 /// 317 /// * Anyone.318 /// 319 /// # Arguments320 /// 321 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.322 /// 323 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.324 /// 325 /// * token_prefix: UTF-8 string with token prefix.326 /// 327 /// * mode: [CollectionMode] collection type and type dependent data.328 // returns collection ID329 #[weight =330 (70_000_000 as Weight)331 .saturating_add(RocksDbWeight::get().reads(7 as Weight))332 .saturating_add(RocksDbWeight::get().writes(5 as Weight))]333 pub fn create_collection(origin,334 collection_name: Vec<u16>,335 collection_description: Vec<u16>,336 token_prefix: Vec<u8>,337 mode: CollectionMode) -> DispatchResult {338339 // Anyone can create a collection340 let who = ensure_signed(origin)?;341 let custom_data_size = match mode {342 CollectionMode::NFT(size) => {343344 // bound Custom data size345 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");346 size347 },348 CollectionMode::ReFungible(size, _) => {349350 // bound Custom data size351 ensure!(size < ChainLimit::get().custom_data_limit, "Custom data size bound exceeded");352 size353 },354 _ => 0355 };356357 let decimal_points = match mode {358 CollectionMode::Fungible(points) => points,359 CollectionMode::ReFungible(_, points) => points,360 _ => 0361 };362363 // bound Total number of collections364 ensure!(CollectionCount::get() < ChainLimit::get().collection_numbers_limit, "Total collections bound exceeded");365366 // check params367 ensure!(decimal_points <= 4, "decimal_points parameter must be lower than 4");368369 let mut name = collection_name.to_vec();370 name.push(0);371 ensure!(name.len() <= 64, "Collection name can not be longer than 63 char");372373 let mut description = collection_description.to_vec();374 description.push(0);375 ensure!(name.len() <= 256, "Collection description can not be longer than 255 char");376377 let mut prefix = token_prefix.to_vec();378 prefix.push(0);379 ensure!(prefix.len() <= 16, "Token prefix can not be longer than 15 char");380381 // Generate next collection ID382 let next_id = CreatedCollectionCount::get()383 .checked_add(1)384 .expect("collection id error");385386 // bound counter387 let total = CollectionCount::get()388 .checked_add(1)389 .expect("collection counter error");390391 CreatedCollectionCount::put(next_id);392 CollectionCount::put(total);393394 // Create new collection395 let new_collection = CollectionType {396 owner: who.clone(),397 name: name,398 mode: mode.clone(),399 mint_mode: false,400 access: AccessMode::Normal,401 description: description,402 decimal_points: decimal_points,403 token_prefix: prefix,404 offchain_schema: Vec::new(),405 custom_data_size: custom_data_size,406 sponsor: T::AccountId::default(),407 unconfirmed_sponsor: T::AccountId::default(),408 };409410 // Add new collection to map411 <Collection<T>>::insert(next_id, new_collection);412413 // call event414 Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));415416 Ok(())417 }418419 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.420 /// 421 /// # Permissions422 /// 423 /// * Collection Owner.424 /// 425 /// # Arguments426 /// 427 /// * collection_id: collection to destroy.428 #[weight =429 (90_000_000 as Weight)430 .saturating_add(RocksDbWeight::get().reads(2 as Weight))431 .saturating_add(RocksDbWeight::get().writes(5 as Weight))]432 pub fn destroy_collection(origin, collection_id: u64) -> DispatchResult {433434 let sender = ensure_signed(origin)?;435 Self::check_owner_permissions(collection_id, sender)?;436437 <AddressTokens<T>>::remove_prefix(collection_id);438 <ApprovedList<T>>::remove_prefix(collection_id);439 <Balance<T>>::remove_prefix(collection_id);440 <ItemListIndex>::remove(collection_id);441 <AdminList<T>>::remove(collection_id);442 <Collection<T>>::remove(collection_id);443 <WhiteList<T>>::remove(collection_id);444445 <NftItemList<T>>::remove_prefix(collection_id);446 <FungibleItemList<T>>::remove_prefix(collection_id);447 <ReFungibleItemList<T>>::remove_prefix(collection_id);448449 <NftTransferBasket<T>>::remove_prefix(collection_id);450 <FungibleTransferBasket<T>>::remove_prefix(collection_id);451 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);452453 if CollectionCount::get() > 0454 {455 // bound couter456 let total = CollectionCount::get()457 .checked_sub(1)458 .expect("collection counter error");459460 CollectionCount::put(total);461 }462463 Ok(())464 }465466 /// Add an address to white list.467 /// 468 /// # Permissions469 /// 470 /// * Collection Owner471 /// * Collection Admin472 /// 473 /// # Arguments474 /// 475 /// * collection_id.476 /// 477 /// * address.478 #[weight =479 (30_000_000 as Weight)480 .saturating_add(RocksDbWeight::get().reads(3 as Weight))481 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]482 pub fn add_to_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{483484 let sender = ensure_signed(origin)?;485 Self::check_owner_or_admin_permissions(collection_id, sender)?;486487 let mut white_list_collection: Vec<T::AccountId>;488 if <WhiteList<T>>::contains_key(collection_id) {489 white_list_collection = <WhiteList<T>>::get(collection_id);490 if !white_list_collection.contains(&address.clone())491 {492 white_list_collection.push(address.clone());493 }494 }495 else {496 white_list_collection = Vec::new();497 white_list_collection.push(address.clone());498 }499500 <WhiteList<T>>::insert(collection_id, white_list_collection);501 Ok(())502 }503504 /// Remove an address from white list.505 /// 506 /// # Permissions507 /// 508 /// * Collection Owner509 /// * Collection Admin510 /// 511 /// # Arguments512 /// 513 /// * collection_id.514 /// 515 /// * address.516 #[weight =517 (35_000_000 as Weight)518 .saturating_add(RocksDbWeight::get().reads(3 as Weight))519 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]520 pub fn remove_from_white_list(origin, collection_id: u64, address: T::AccountId) -> DispatchResult{521522 let sender = ensure_signed(origin)?;523 Self::check_owner_or_admin_permissions(collection_id, sender)?;524525 if <WhiteList<T>>::contains_key(collection_id) {526 let mut white_list_collection = <WhiteList<T>>::get(collection_id);527 if white_list_collection.contains(&address.clone())528 {529 white_list_collection.retain(|i| *i != address.clone());530 <WhiteList<T>>::insert(collection_id, white_list_collection);531 }532 }533534 Ok(())535 }536537 /// Toggle between normal and white list access for the methods with access for `Anyone`.538 /// 539 /// # Permissions540 /// 541 /// * Collection Owner.542 /// 543 /// # Arguments544 /// 545 /// * collection_id.546 /// 547 /// * mode: [AccessMode]548 #[weight =549 (27_000_000 as Weight)550 .saturating_add(RocksDbWeight::get().reads(1 as Weight))551 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]552 pub fn set_public_access_mode(origin, collection_id: u64, mode: AccessMode) -> DispatchResult553 {554 let sender = ensure_signed(origin)?;555556 Self::check_owner_permissions(collection_id, sender)?;557 let mut target_collection = <Collection<T>>::get(collection_id);558 target_collection.access = mode;559 <Collection<T>>::insert(collection_id, target_collection);560561 Ok(())562 }563564 /// Allows Anyone to create tokens if:565 /// * White List is enabled, and566 /// * Address is added to white list, and567 /// * This method was called with True parameter568 /// 569 /// # Permissions570 /// * Collection Owner571 ///572 /// # Arguments573 /// 574 /// * collection_id.575 /// 576 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.577 #[weight =578 (27_000_000 as Weight)579 .saturating_add(RocksDbWeight::get().reads(1 as Weight))580 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]581 pub fn set_mint_permission(origin, collection_id: u64, mint_permission: bool) -> DispatchResult582 {583 let sender = ensure_signed(origin)?;584585 Self::check_owner_permissions(collection_id, sender)?;586 let mut target_collection = <Collection<T>>::get(collection_id);587 target_collection.mint_mode = mint_permission;588 <Collection<T>>::insert(collection_id, target_collection);589590 Ok(())591 }592593 /// Change the owner of the collection.594 /// 595 /// # Permissions596 /// 597 /// * Collection Owner.598 /// 599 /// # Arguments600 /// 601 /// * collection_id.602 /// 603 /// * new_owner.604 #[weight =605 (27_000_000 as Weight)606 .saturating_add(RocksDbWeight::get().reads(1 as Weight))607 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]608 pub fn change_collection_owner(origin, collection_id: u64, new_owner: T::AccountId) -> DispatchResult {609610 let sender = ensure_signed(origin)?;611 Self::check_owner_permissions(collection_id, sender)?;612 let mut target_collection = <Collection<T>>::get(collection_id);613 target_collection.owner = new_owner;614 <Collection<T>>::insert(collection_id, target_collection);615616 Ok(())617 }618619 /// Adds an admin of the Collection.620 /// 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. 621 /// 622 /// # Permissions623 /// 624 /// * Collection Owner.625 /// * Collection Admin.626 /// 627 /// # Arguments628 /// 629 /// * collection_id: ID of the Collection to add admin for.630 /// 631 /// * new_admin_id: Address of new admin to add.632 #[weight =633 (32_000_000 as Weight)634 .saturating_add(RocksDbWeight::get().reads(3 as Weight))635 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]636 pub fn add_collection_admin(origin, collection_id: u64, new_admin_id: T::AccountId) -> DispatchResult {637638 let sender = ensure_signed(origin)?;639 Self::check_owner_or_admin_permissions(collection_id, sender)?;640 let mut admin_arr: Vec<T::AccountId> = Vec::new();641642 if <AdminList<T>>::contains_key(collection_id)643 {644 admin_arr = <AdminList<T>>::get(collection_id);645 ensure!(!admin_arr.contains(&new_admin_id), "Account already has admin role");646 }647648 // Number of collection admins649 ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, "Number of collection admins bound exceeded");650651 admin_arr.push(new_admin_id);652 <AdminList<T>>::insert(collection_id, admin_arr);653654 Ok(())655 }656657 /// 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.658 ///659 /// # Permissions660 /// 661 /// * Collection Owner.662 /// * Collection Admin.663 /// 664 /// # Arguments665 /// 666 /// * collection_id: ID of the Collection to remove admin for.667 /// 668 /// * account_id: Address of admin to remove.669 #[weight =670 (50_000_000 as Weight)671 .saturating_add(RocksDbWeight::get().reads(2 as Weight))672 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]673 pub fn remove_collection_admin(origin, collection_id: u64, account_id: T::AccountId) -> DispatchResult {674675 let sender = ensure_signed(origin)?;676 Self::check_owner_or_admin_permissions(collection_id, sender)?;677678 if <AdminList<T>>::contains_key(collection_id)679 {680 let mut admin_arr = <AdminList<T>>::get(collection_id);681 admin_arr.retain(|i| *i != account_id);682 <AdminList<T>>::insert(collection_id, admin_arr);683 }684685 Ok(())686 }687688 /// # Permissions689 /// 690 /// * Collection Owner691 /// 692 /// # Arguments693 /// 694 /// * collection_id.695 /// 696 /// * new_sponsor.697 #[weight =698 (32_000_000 as Weight)699 .saturating_add(RocksDbWeight::get().reads(2 as Weight))700 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]701 pub fn set_collection_sponsor(origin, collection_id: u64, new_sponsor: T::AccountId) -> DispatchResult {702703 let sender = ensure_signed(origin)?;704 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");705706 let mut target_collection = <Collection<T>>::get(collection_id);707 ensure!(sender == target_collection.owner, "You do not own this collection");708709 target_collection.unconfirmed_sponsor = new_sponsor;710 <Collection<T>>::insert(collection_id, target_collection);711712 Ok(())713 }714715 /// # Permissions716 /// 717 /// * Sponsor.718 /// 719 /// # Arguments720 /// 721 /// * collection_id.722 #[weight =723 (22_000_000 as Weight)724 .saturating_add(RocksDbWeight::get().reads(1 as Weight))725 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]726 pub fn confirm_sponsorship(origin, collection_id: u64) -> DispatchResult {727728 let sender = ensure_signed(origin)?;729 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");730731 let mut target_collection = <Collection<T>>::get(collection_id);732 ensure!(sender == target_collection.unconfirmed_sponsor, "This address is not set as sponsor, use setCollectionSponsor first");733734 target_collection.sponsor = target_collection.unconfirmed_sponsor;735 target_collection.unconfirmed_sponsor = T::AccountId::default();736 <Collection<T>>::insert(collection_id, target_collection);737738 Ok(())739 }740741 /// Switch back to pay-per-own-transaction model.742 ///743 /// # Permissions744 ///745 /// * Collection owner.746 /// 747 /// # Arguments748 /// 749 /// * collection_id.750 #[weight =751 (24_000_000 as Weight)752 .saturating_add(RocksDbWeight::get().reads(1 as Weight))753 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]754 pub fn remove_collection_sponsor(origin, collection_id: u64) -> DispatchResult {755756 let sender = ensure_signed(origin)?;757 ensure!(<Collection<T>>::contains_key(collection_id), "This collection does not exist");758759 let mut target_collection = <Collection<T>>::get(collection_id);760 ensure!(sender == target_collection.owner, "You do not own this collection");761762 target_collection.sponsor = T::AccountId::default();763 <Collection<T>>::insert(collection_id, target_collection);764765 Ok(())766 }767768 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.769 /// 770 /// # Permissions771 /// 772 /// * Collection Owner.773 /// * Collection Admin.774 /// * Anyone if775 /// * White List is enabled, and776 /// * Address is added to white list, and777 /// * MintPermission is enabled (see SetMintPermission method)778 /// 779 /// # Arguments780 /// 781 /// * collection_id: ID of the collection.782 /// 783 /// * properties: Array of bytes that contains NFT properties. Since NFT Module is agnostic of properties meaning, it is treated purely as an array of bytes.784 /// 785 /// * owner: Address, initial owner of the NFT.786 #[weight =787 (130_000_000 as Weight)788 .saturating_add(RocksDbWeight::get().reads(10 as Weight))789 .saturating_add(RocksDbWeight::get().writes(8 as Weight))]790 pub fn create_item(origin, collection_id: u64, properties: Vec<u8>, owner: T::AccountId) -> DispatchResult {791792 let sender = ensure_signed(origin)?;793 Self::collection_exists(collection_id)?;794 let target_collection = <Collection<T>>::get(collection_id);795796 if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {797 ensure!(target_collection.mint_mode == true, "Public minting is not allowed for this collection");798 Self::check_white_list(collection_id, &owner)?;799 Self::check_white_list(collection_id, &sender)?;800 }801802 match target_collection.mode803 {804 CollectionMode::NFT(_) => {805806 // check size807 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");808809 // Create nft item810 let item = NftItemType {811 collection: collection_id,812 owner: owner,813 data: properties.clone(),814 };815816 Self::add_nft_item(item)?;817818 },819 CollectionMode::Fungible(_) => {820821 // check size822 ensure!(properties.len() as u32 == 0, "Size of item must be 0 with fungible type");823824 let item = FungibleItemType {825 collection: collection_id,826 owner: owner,827 value: (10 as u128).pow(target_collection.decimal_points)828 };829830 Self::add_fungible_item(item)?;831 },832 CollectionMode::ReFungible(_, _) => {833834 // check size835 ensure!(target_collection.custom_data_size >= properties.len() as u32, "Size of item is too large");836837 let mut owner_list = Vec::new();838 let value = (10 as u128).pow(target_collection.decimal_points);839 owner_list.push(Ownership {owner: owner.clone(), fraction: value});840841 let item = ReFungibleItemType {842 collection: collection_id,843 owner: owner_list,844 data: properties.clone()845 };846847 Self::add_refungible_item(item)?;848 },849 _ => { ensure!(1 == 0,"just error"); }850851 };852853 // call event854 Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id)));855856 Ok(())857 }858859 /// Destroys a concrete instance of NFT.860 /// 861 /// # Permissions862 /// 863 /// * Collection Owner.864 /// * Collection Admin.865 /// * Current NFT Owner.866 /// 867 /// # Arguments868 /// 869 /// * collection_id: ID of the collection.870 /// 871 /// * item_id: ID of NFT to burn.872 #[weight =873 (170_000_000 as Weight)874 .saturating_add(RocksDbWeight::get().reads(9 as Weight))875 .saturating_add(RocksDbWeight::get().writes(7 as Weight))]876 pub fn burn_item(origin, collection_id: u64, item_id: u64) -> DispatchResult {877878 let sender = ensure_signed(origin)?;879 Self::collection_exists(collection_id)?;880881 // Transfer permissions check882 let target_collection = <Collection<T>>::get(collection_id);883 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||884 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),885 "Only item owner, collection owner and admins can modify item");886887 if target_collection.access == AccessMode::WhiteList {888 Self::check_white_list(collection_id, &sender)?;889 }890891 match target_collection.mode892 {893 CollectionMode::NFT(_) => Self::burn_nft_item(collection_id, item_id)?,894 CollectionMode::Fungible(_) => Self::burn_fungible_item(collection_id, item_id)?,895 CollectionMode::ReFungible(_, _) => Self::burn_refungible_item(collection_id, item_id, sender.clone())?,896 _ => ()897 };898899 // call event900 Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));901902 Ok(())903 }904905 /// Change ownership of the token.906 /// 907 /// # Permissions908 /// 909 /// * Collection Owner910 /// * Collection Admin911 /// * Current NFT owner912 ///913 /// # Arguments914 /// 915 /// * recipient: Address of token recipient.916 /// 917 /// * collection_id.918 /// 919 /// * item_id: ID of the item920 /// * Non-Fungible Mode: Required.921 /// * Fungible Mode: Ignored.922 /// * Re-Fungible Mode: Required.923 /// 924 /// * value: Amount to transfer.925 /// * Non-Fungible Mode: Ignored926 /// * Fungible Mode: Must specify transferred amount927 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)928 #[weight =929 (125_000_000 as Weight)930 .saturating_add(RocksDbWeight::get().reads(7 as Weight))931 .saturating_add(RocksDbWeight::get().writes(7 as Weight))]932 pub fn transfer(origin, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64) -> DispatchResult {933934 let sender = ensure_signed(origin)?;935936 // Transfer permissions check937 let target_collection = <Collection<T>>::get(collection_id);938 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||939 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),940 "Only item owner, collection owner and admins can modify item");941942 if target_collection.access == AccessMode::WhiteList {943 Self::check_white_list(collection_id, &sender)?;944 Self::check_white_list(collection_id, &recipient)?;945 }946947 match target_collection.mode948 {949 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient)?,950 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, sender.clone(), recipient)?,951 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient)?,952 _ => ()953 };954955 Ok(())956 }957958 /// Set, change, or remove approved address to transfer the ownership of the NFT.959 /// 960 /// # Permissions961 /// 962 /// * Collection Owner963 /// * Collection Admin964 /// * Current NFT owner965 /// 966 /// # Arguments967 /// 968 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).969 /// 970 /// * collection_id.971 /// 972 /// * item_id: ID of the item.973 #[weight =974 (45_000_000 as Weight)975 .saturating_add(RocksDbWeight::get().reads(3 as Weight))976 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]977 pub fn approve(origin, approved: T::AccountId, collection_id: u64, item_id: u64) -> DispatchResult {978979 let sender = ensure_signed(origin)?;980981 // Transfer permissions check982 let target_collection = <Collection<T>>::get(collection_id);983 ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||984 Self::is_owner_or_admin_permissions(collection_id, sender.clone()),985 "Only item owner, collection owner and admins can approve");986987 if target_collection.access == AccessMode::WhiteList {988 Self::check_white_list(collection_id, &sender)?;989 Self::check_white_list(collection_id, &approved)?;990 }991992 // amount param stub993 let amount = 100000000;994995 let list_exists = <ApprovedList<T>>::contains_key(collection_id, (item_id, sender.clone()));996 if list_exists {997998 let mut list = <ApprovedList<T>>::get(collection_id, (item_id, sender.clone()));999 let item_contains = list.iter().any(|i| i.approved == approved);10001001 if !item_contains {1002 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1003 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1004 }1005 } else {10061007 let mut list = Vec::new();1008 list.push(ApprovePermissions { approved: approved.clone(), amount: amount });1009 <ApprovedList<T>>::insert(collection_id, (item_id, sender.clone()), list);1010 }10111012 Ok(())1013 }1014 1015 /// 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.1016 /// 1017 /// # Permissions1018 /// * Collection Owner1019 /// * Collection Admin1020 /// * Current NFT owner1021 /// * Address approved by current NFT owner1022 /// 1023 /// # Arguments1024 /// 1025 /// * from: Address that owns token.1026 /// 1027 /// * recipient: Address of token recipient.1028 /// 1029 /// * collection_id.1030 /// 1031 /// * item_id: ID of the item.1032 /// 1033 /// * value: Amount to transfer.1034 #[weight =1035 (150_000_000 as Weight)1036 .saturating_add(RocksDbWeight::get().reads(9 as Weight))1037 .saturating_add(RocksDbWeight::get().writes(8 as Weight))]1038 pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: u64, item_id: u64, value: u64 ) -> DispatchResult {10391040 let sender = ensure_signed(origin)?;1041 let mut appoved_transfer = false;10421043 // Check approve1044 if <ApprovedList<T>>::contains_key(collection_id, (item_id, from.clone())) {1045 let list_itm = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()));1046 let opt_item = list_itm.iter().find(|i| i.approved == sender.clone());1047 if opt_item.is_some()1048 {1049 appoved_transfer = true;1050 ensure!(opt_item.unwrap().amount >= value, "Requested value more than approved");1051 }1052 }10531054 // Transfer permissions check1055 let target_collection = <Collection<T>>::get(collection_id);1056 ensure!(appoved_transfer || Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1057 "Only item owner, collection owner and admins can modify items");10581059 if target_collection.access == AccessMode::WhiteList {1060 Self::check_white_list(collection_id, &sender)?;1061 Self::check_white_list(collection_id, &recipient)?;1062 }10631064 // remove approve1065 let approve_list: Vec<ApprovePermissions<T::AccountId>> = <ApprovedList<T>>::get(collection_id, (item_id, from.clone()))1066 .into_iter().filter(|i| i.approved != sender.clone()).collect();1067 <ApprovedList<T>>::insert(collection_id, (item_id, from.clone()), approve_list);106810691070 match target_collection.mode1071 {1072 CollectionMode::NFT(_) => Self::transfer_nft(collection_id, item_id, from, recipient)?,1073 CollectionMode::Fungible(_) => Self::transfer_fungible(collection_id, item_id, value, from.clone(), recipient)?,1074 CollectionMode::ReFungible(_, _) => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1075 _ => ()1076 };10771078 Ok(())1079 }10801081 ///1082 #[weight = 0]1083 pub fn safe_transfer_from(origin, collection_id: u64, item_id: u64, new_owner: T::AccountId) -> DispatchResult {10841085 // let no_perm_mes = "You do not have permissions to modify this collection";1086 // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1087 // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1088 // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10891090 // // on_nft_received call10911092 // Self::transfer(origin, collection_id, item_id, new_owner)?;10931094 Ok(())1095 }10961097 /// Set off-chain data schema.1098 /// 1099 /// # Permissions1100 /// 1101 /// * Collection Owner1102 /// * Collection Admin1103 /// 1104 /// # Arguments1105 /// 1106 /// * collection_id.1107 /// 1108 /// * schema: String representing the offchain data schema.1109 #[weight =1110 (33_000_000 as Weight)1111 .saturating_add(RocksDbWeight::get().reads(2 as Weight))1112 .saturating_add(RocksDbWeight::get().writes(1 as Weight))]1113 pub fn set_offchain_schema(1114 origin,1115 collection_id: u64,1116 schema: Vec<u8>1117 ) -> DispatchResult {1118 let sender = ensure_signed(origin)?;1119 Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;11201121 let mut target_collection = <Collection<T>>::get(collection_id);1122 target_collection.offchain_schema = schema;1123 <Collection<T>>::insert(collection_id, target_collection);11241125 Ok(())1126 }11271128 // Sudo permissions function1129 #[weight = 0]1130 pub fn set_chain_limits(1131 origin,1132 limits: ChainLimits1133 ) -> DispatchResult {1134 ensure_root(origin)?;1135 <ChainLimit>::put(limits);1136 Ok(())1137 } 1138 }1139}11401141impl<T: Trait> Module<T> {1142 fn add_fungible_item(item: FungibleItemType<T::AccountId>) -> DispatchResult {1143 let current_index = <ItemListIndex>::get(item.collection)1144 .checked_add(1)1145 .expect("Item list index id error");1146 let itemcopy = item.clone();1147 let owner = item.owner.clone();1148 let value = item.value as u64;11491150 Self::add_token_index(item.collection, current_index, owner.clone())?;11511152 <ItemListIndex>::insert(item.collection, current_index);1153 <FungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11541155 // Add current block1156 let v: Vec<BasketItem<T::AccountId, T::BlockNumber>> = Vec::new();1157 <FungibleTransferBasket<T>>::insert(item.collection, current_index, v);1158 1159 // Update balance1160 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1161 .checked_add(value)1162 .unwrap();1163 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11641165 Ok(())1166 }11671168 fn add_refungible_item(item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1169 let current_index = <ItemListIndex>::get(item.collection)1170 .checked_add(1)1171 .expect("Item list index id error");1172 let itemcopy = item.clone();11731174 let value = item.owner.first().unwrap().fraction as u64;1175 let owner = item.owner.first().unwrap().owner.clone();11761177 Self::add_token_index(item.collection, current_index, owner.clone())?;11781179 <ItemListIndex>::insert(item.collection, current_index);1180 <ReFungibleItemList<T>>::insert(item.collection, current_index, itemcopy);11811182 // Add current block1183 let block_number: T::BlockNumber = 0.into();1184 <ReFungibleTransferBasket<T>>::insert(item.collection, current_index, block_number);11851186 // Update balance1187 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1188 .checked_add(value)1189 .unwrap();1190 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);11911192 Ok(())1193 }11941195 fn add_nft_item(item: NftItemType<T::AccountId>) -> DispatchResult {1196 let current_index = <ItemListIndex>::get(item.collection)1197 .checked_add(1)1198 .expect("Item list index id error");11991200 let item_owner = item.owner.clone();1201 let collection_id = item.collection.clone();1202 Self::add_token_index(collection_id, current_index, item.owner.clone())?;12031204 <ItemListIndex>::insert(collection_id, current_index);1205 <NftItemList<T>>::insert(collection_id, current_index, item);12061207 // Add current block1208 let block_number: T::BlockNumber = 0.into();1209 <NftTransferBasket<T>>::insert(collection_id, current_index, block_number);12101211 // Update balance1212 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1213 .checked_add(1)1214 .unwrap();1215 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);12161217 Ok(())1218 }12191220 fn burn_refungible_item(1221 collection_id: u64,1222 item_id: u64,1223 owner: T::AccountId,1224 ) -> DispatchResult {1225 ensure!(1226 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1227 "Item does not exists"1228 );1229 let collection = <ReFungibleItemList<T>>::get(collection_id, item_id);1230 let item = collection1231 .owner1232 .iter()1233 .filter(|&i| i.owner == owner)1234 .next()1235 .unwrap();1236 Self::remove_token_index(collection_id, item_id, owner.clone())?;12371238 // remove approve list1239 <ApprovedList<T>>::remove(collection_id, (item_id, owner.clone()));12401241 // update balance1242 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1243 .checked_sub(item.fraction as u64)1244 .unwrap();1245 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12461247 <ReFungibleItemList<T>>::remove(collection_id, item_id);12481249 Ok(())1250 }12511252 fn burn_nft_item(collection_id: u64, item_id: u64) -> DispatchResult {1253 ensure!(1254 <NftItemList<T>>::contains_key(collection_id, item_id),1255 "Item does not exists"1256 );1257 let item = <NftItemList<T>>::get(collection_id, item_id);1258 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12591260 // remove approve list1261 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12621263 // update balance1264 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1265 .checked_sub(1)1266 .unwrap();1267 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1268 <NftItemList<T>>::remove(collection_id, item_id);12691270 Ok(())1271 }12721273 fn burn_fungible_item(collection_id: u64, item_id: u64) -> DispatchResult {1274 ensure!(1275 <FungibleItemList<T>>::contains_key(collection_id, item_id),1276 "Item does not exists"1277 );1278 let item = <FungibleItemList<T>>::get(collection_id, item_id);1279 Self::remove_token_index(collection_id, item_id, item.owner.clone())?;12801281 // remove approve list1282 <ApprovedList<T>>::remove(collection_id, (item_id, item.owner.clone()));12831284 // update balance1285 let new_balance = <Balance<T>>::get(collection_id, item.owner.clone())1286 .checked_sub(item.value as u64)1287 .unwrap();1288 <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);12891290 <FungibleItemList<T>>::remove(collection_id, item_id);12911292 Ok(())1293 }12941295 fn collection_exists(collection_id: u64) -> DispatchResult {1296 ensure!(1297 <Collection<T>>::contains_key(collection_id),1298 "This collection does not exist"1299 );1300 Ok(())1301 }13021303 fn check_owner_permissions(collection_id: u64, subject: T::AccountId) -> DispatchResult {1304 Self::collection_exists(collection_id)?;13051306 let target_collection = <Collection<T>>::get(collection_id);1307 ensure!(1308 subject == target_collection.owner,1309 "You do not own this collection"1310 );13111312 Ok(())1313 }13141315 fn is_owner_or_admin_permissions(collection_id: u64, subject: T::AccountId) -> bool {1316 let target_collection = <Collection<T>>::get(collection_id);1317 let mut result: bool = subject == target_collection.owner;1318 let exists = <AdminList<T>>::contains_key(collection_id);13191320 if !result & exists {1321 if <AdminList<T>>::get(collection_id).contains(&subject) {1322 result = true1323 }1324 }13251326 result1327 }13281329 fn check_owner_or_admin_permissions(1330 collection_id: u64,1331 subject: T::AccountId,1332 ) -> DispatchResult {1333 Self::collection_exists(collection_id)?;1334 let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());13351336 ensure!(1337 result,1338 "You do not have permissions to modify this collection"1339 );1340 Ok(())1341 }13421343 fn is_item_owner(subject: T::AccountId, collection_id: u64, item_id: u64) -> bool {1344 let target_collection = <Collection<T>>::get(collection_id);13451346 match target_collection.mode {1347 CollectionMode::NFT(_) => {1348 <NftItemList<T>>::get(collection_id, item_id).owner == subject1349 }1350 CollectionMode::Fungible(_) => {1351 <FungibleItemList<T>>::get(collection_id, item_id).owner == subject1352 }1353 CollectionMode::ReFungible(_, _) => {1354 <ReFungibleItemList<T>>::get(collection_id, item_id)1355 .owner1356 .iter()1357 .any(|i| i.owner == subject)1358 }1359 CollectionMode::Invalid => false,1360 }1361 }13621363 fn check_white_list(collection_id: u64, address: &T::AccountId) -> DispatchResult {1364 let mes = "Address is not in white list";1365 ensure!(<WhiteList<T>>::contains_key(collection_id), mes);1366 let wl = <WhiteList<T>>::get(collection_id);1367 ensure!(wl.contains(address), mes);13681369 Ok(())1370 }13711372 fn transfer_fungible(1373 collection_id: u64,1374 item_id: u64,1375 value: u64,1376 owner: T::AccountId,1377 new_owner: T::AccountId,1378 ) -> DispatchResult {1379 ensure!(1380 <FungibleItemList<T>>::contains_key(collection_id, item_id),1381 "Item not exists"1382 );13831384 let full_item = <FungibleItemList<T>>::get(collection_id, item_id);1385 let amount = full_item.value;13861387 ensure!(amount >= value.into(), "Item balance not enouth");13881389 // update balance1390 let balance_old_owner = <Balance<T>>::get(collection_id, owner.clone())1391 .checked_sub(value)1392 .unwrap();1393 <Balance<T>>::insert(collection_id, owner.clone(), balance_old_owner);13941395 let mut new_owner_account_id = 0;1396 let new_owner_items = <AddressTokens<T>>::get(collection_id, new_owner.clone());1397 if new_owner_items.len() > 0 {1398 new_owner_account_id = new_owner_items[0];1399 }14001401 let val64 = value.into();14021403 // transfer1404 if amount == val64 && new_owner_account_id == 0 {1405 // change owner1406 // new owner do not have account1407 let mut new_full_item = full_item.clone();1408 new_full_item.owner = new_owner.clone();1409 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);14101411 // update balance1412 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1413 .checked_add(value)1414 .unwrap();1415 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14161417 // update index collection1418 Self::move_token_index(collection_id, item_id, owner.clone(), new_owner.clone())?;1419 } else {1420 let mut new_full_item = full_item.clone();1421 new_full_item.value -= val64;14221423 // separate amount1424 if new_owner_account_id > 0 {1425 // new owner has account1426 let mut item = <FungibleItemList<T>>::get(collection_id, new_owner_account_id);1427 item.value += val64;14281429 // update balance1430 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1431 .checked_add(value)1432 .unwrap();1433 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14341435 <FungibleItemList<T>>::insert(collection_id, new_owner_account_id, item);1436 } else {1437 // new owner do not have account1438 let item = FungibleItemType {1439 collection: collection_id,1440 owner: new_owner.clone(),1441 value: val64,1442 };14431444 Self::add_fungible_item(item)?;1445 }14461447 if amount == val64 {1448 Self::remove_token_index(collection_id, item_id, full_item.owner.clone())?;14491450 // remove approve list1451 <ApprovedList<T>>::remove(collection_id, (item_id, full_item.owner.clone()));1452 <FungibleItemList<T>>::remove(collection_id, item_id);1453 }14541455 <FungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1456 }14571458 Ok(())1459 }14601461 fn transfer_refungible(1462 collection_id: u64,1463 item_id: u64,1464 value: u64,1465 owner: T::AccountId,1466 new_owner: T::AccountId,1467 ) -> DispatchResult {1468 ensure!(1469 <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1470 "Item not exists"1471 );14721473 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);1474 let item = full_item1475 .owner1476 .iter()1477 .filter(|i| i.owner == owner)1478 .next()1479 .unwrap();1480 let amount = item.fraction;14811482 ensure!(amount >= value.into(), "Item balance not enouth");14831484 // update balance1485 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1486 .checked_sub(value)1487 .unwrap();1488 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);14891490 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1491 .checked_add(value)1492 .unwrap();1493 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);14941495 let old_owner = item.owner.clone();1496 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);1497 let val64 = value.into();14981499 // transfer1500 if amount == val64 && !new_owner_has_account {1501 // change owner1502 // new owner do not have account1503 let mut new_full_item = full_item.clone();1504 new_full_item1505 .owner1506 .iter_mut()1507 .find(|i| i.owner == owner)1508 .unwrap()1509 .owner = new_owner.clone();1510 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);15111512 // update index collection1513 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;1514 } else {1515 let mut new_full_item = full_item.clone();1516 new_full_item1517 .owner1518 .iter_mut()1519 .find(|i| i.owner == owner)1520 .unwrap()1521 .fraction -= val64;15221523 // separate amount1524 if new_owner_has_account {1525 // new owner has account1526 new_full_item1527 .owner1528 .iter_mut()1529 .find(|i| i.owner == new_owner)1530 .unwrap()1531 .fraction += val64;1532 } else {1533 // new owner do not have account1534 new_full_item.owner.push(Ownership {1535 owner: new_owner.clone(),1536 fraction: val64,1537 });1538 Self::add_token_index(collection_id, item_id, new_owner.clone())?;1539 }15401541 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);1542 }15431544 Ok(())1545 }15461547 fn transfer_nft(1548 collection_id: u64,1549 item_id: u64,1550 sender: T::AccountId,1551 new_owner: T::AccountId,1552 ) -> DispatchResult {1553 ensure!(1554 <NftItemList<T>>::contains_key(collection_id, item_id),1555 "Item not exists"1556 );15571558 let mut item = <NftItemList<T>>::get(collection_id, item_id);15591560 ensure!(1561 sender == item.owner,1562 "sender parameter and item owner must be equal"1563 );15641565 // update balance1566 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())1567 .checked_sub(1)1568 .unwrap();1569 <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);15701571 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())1572 .checked_add(1)1573 .unwrap();1574 <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);15751576 // change owner1577 let old_owner = item.owner.clone();1578 item.owner = new_owner.clone();1579 <NftItemList<T>>::insert(collection_id, item_id, item);15801581 // update index collection1582 Self::move_token_index(collection_id, item_id, old_owner.clone(), new_owner.clone())?;15831584 // reset approved list1585 <ApprovedList<T>>::remove(collection_id, (item_id, old_owner));1586 Ok(())1587 }15881589 fn init_collection(item: &CollectionType<T::AccountId>) {1590 // check params1591 assert!(1592 item.decimal_points <= 4,1593 "decimal_points parameter must be lower than 4"1594 );1595 assert!(1596 item.name.len() <= 64,1597 "Collection name can not be longer than 63 char"1598 );1599 assert!(1600 item.name.len() <= 256,1601 "Collection description can not be longer than 255 char"1602 );1603 assert!(1604 item.token_prefix.len() <= 16,1605 "Token prefix can not be longer than 15 char"1606 );16071608 // Generate next collection ID1609 let next_id = CreatedCollectionCount::get()1610 .checked_add(1)1611 .expect("collection id error");16121613 CreatedCollectionCount::put(next_id);1614 }16151616 fn init_nft_token(item: &NftItemType<T::AccountId>) {1617 let current_index = <ItemListIndex>::get(item.collection)1618 .checked_add(1)1619 .expect("Item list index id error");16201621 let item_owner = item.owner.clone();1622 let collection_id = item.collection.clone();1623 Self::add_token_index(collection_id, current_index, item.owner.clone()).unwrap();16241625 <ItemListIndex>::insert(collection_id, current_index);16261627 // Update balance1628 let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1629 .checked_add(1)1630 .unwrap();1631 <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);1632 }16331634 fn init_fungible_token(item: &FungibleItemType<T::AccountId>) {1635 let current_index = <ItemListIndex>::get(item.collection)1636 .checked_add(1)1637 .expect("Item list index id error");1638 let owner = item.owner.clone();1639 let value = item.value as u64;16401641 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16421643 <ItemListIndex>::insert(item.collection, current_index);16441645 // Update balance1646 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1647 .checked_add(value)1648 .unwrap();1649 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1650 }16511652 fn init_refungible_token(item: &ReFungibleItemType<T::AccountId>) {1653 let current_index = <ItemListIndex>::get(item.collection)1654 .checked_add(1)1655 .expect("Item list index id error");16561657 let value = item.owner.first().unwrap().fraction as u64;1658 let owner = item.owner.first().unwrap().owner.clone();16591660 Self::add_token_index(item.collection, current_index, owner.clone()).unwrap();16611662 <ItemListIndex>::insert(item.collection, current_index);16631664 // Update balance1665 let new_balance = <Balance<T>>::get(item.collection, owner.clone())1666 .checked_add(value)1667 .unwrap();1668 <Balance<T>>::insert(item.collection, owner.clone(), new_balance);1669 }16701671 fn add_token_index(collection_id: u64, item_index: u64, owner: T::AccountId) -> DispatchResult {16721673 // add to account limit1674 if <AccountItemCount<T>>::contains_key(owner.clone()) {16751676 // bound Owned tokens by a single address1677 let count = <AccountItemCount<T>>::get(owner.clone());1678 ensure!(count < ChainLimit::get().account_token_ownership_limit, "Owned tokens by a single address bound exceeded");16791680 <AccountItemCount<T>>::insert(owner.clone(), 1681 count.checked_add(1).unwrap());1682 }1683 else {1684 <AccountItemCount<T>>::insert(owner.clone(), 1);1685 }16861687 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1688 if list_exists {1689 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1690 let item_contains = list.contains(&item_index.clone());16911692 if !item_contains {1693 list.push(item_index.clone());1694 }16951696 <AddressTokens<T>>::insert(collection_id, owner.clone(), list);1697 } else {1698 let mut itm = Vec::new();1699 itm.push(item_index.clone());1700 <AddressTokens<T>>::insert(collection_id, owner, itm);1701 1702 }17031704 Ok(())1705 }17061707 fn remove_token_index(1708 collection_id: u64,1709 item_index: u64,1710 owner: T::AccountId,1711 ) -> DispatchResult {17121713 // update counter1714 <AccountItemCount<T>>::insert(owner.clone(), 1715 <AccountItemCount<T>>::get(owner.clone()).checked_sub(1).unwrap());171617171718 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.clone());1719 if list_exists {1720 let mut list = <AddressTokens<T>>::get(collection_id, owner.clone());1721 let item_contains = list.contains(&item_index.clone());17221723 if item_contains {1724 list.retain(|&item| item != item_index);1725 <AddressTokens<T>>::insert(collection_id, owner, list);1726 }1727 }17281729 Ok(())1730 }17311732 fn move_token_index(1733 collection_id: u64,1734 item_index: u64,1735 old_owner: T::AccountId,1736 new_owner: T::AccountId,1737 ) -> DispatchResult {1738 Self::remove_token_index(collection_id, item_index, old_owner)?;1739 Self::add_token_index(collection_id, item_index, new_owner)?;17401741 Ok(())1742 }1743}17441745////////////////////////////////////////////////////////////////////////////////////////////////////1746// Economic models1747// #region17481749/// Fee multiplier.1750pub type Multiplier = FixedU128;17511752type BalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1753 <T as system::Trait>::AccountId,1754>>::Balance;1755type NegativeImbalanceOf<T> = <<T as transaction_payment::Trait>::Currency as Currency<1756 <T as system::Trait>::AccountId,1757>>::NegativeImbalance;17581759/// Require the transactor pay for themselves and maybe include a tip to gain additional priority1760/// in the queue.1761#[derive(Encode, Decode, Clone, Eq, PartialEq)]1762pub struct ChargeTransactionPayment<T: transaction_payment::Trait + Send + Sync>(1763 #[codec(compact)] BalanceOf<T>,1764);17651766impl<T: Trait + transaction_payment::Trait + Send + Sync> sp_std::fmt::Debug1767 for ChargeTransactionPayment<T>1768{1769 #[cfg(feature = "std")]1770 fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1771 write!(f, "ChargeTransactionPayment<{:?}>", self.0)1772 }1773 #[cfg(not(feature = "std"))]1774 fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {1775 Ok(())1776 }1777}17781779impl<T: Trait + transaction_payment::Trait + Send + Sync> ChargeTransactionPayment<T>1780where1781 T::Call:1782 Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1783 BalanceOf<T>: Send + Sync + FixedPointOperand,1784{1785 /// utility constructor. Used only in client/factory code.1786 pub fn from(fee: BalanceOf<T>) -> Self {1787 Self(fee)1788 }17891790 pub fn traditional_fee(1791 len: usize,1792 info: &DispatchInfoOf<T::Call>,1793 tip: BalanceOf<T>,1794 ) -> BalanceOf<T>1795 where1796 T::Call: Dispatchable<Info = DispatchInfo>,1797 {1798 <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)1799 }18001801 fn withdraw_fee(1802 &self,1803 who: &T::AccountId,1804 call: &T::Call,1805 info: &DispatchInfoOf<T::Call>,1806 len: usize,1807 ) -> Result<(BalanceOf<T>, Option<NegativeImbalanceOf<T>>), TransactionValidityError> {1808 let tip = self.0;18091810 // Set fee based on call type. Creating collection costs 1 Unique.1811 // All other transactions have traditional fees so far1812 let fee = match call.is_sub_type() {1813 Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),1814 _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes1815 // _ => <BalanceOf<T>>::from(100)1816 };18171818 // Determine who is paying transaction fee based on ecnomic model1819 // Parse call to extract collection ID and access collection sponsor1820 let sponsor: T::AccountId = match call.is_sub_type() {1821 Some(Call::create_item(collection_id, _properties, _owner)) => {1822 <Collection<T>>::get(collection_id).sponsor1823 }1824 Some(Call::transfer(_new_owner, collection_id, _item_id, _value)) => {1825 let _collection_mode = <Collection<T>>::get(collection_id).mode;18261827 // sponsor timeout1828 let sponsor_transfer = match _collection_mode {1829 CollectionMode::NFT(_) => {1830 let basket = <NftTransferBasket<T>>::get(collection_id, _item_id);1831 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1832 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1833 if block_number >= limit_time {1834 <NftTransferBasket<T>>::insert(collection_id, _item_id, block_number);1835 true1836 }1837 else {1838 false1839 }1840 }1841 CollectionMode::Fungible(_) => {1842 let mut basket = <FungibleTransferBasket<T>>::get(collection_id, _item_id);1843 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1844 if basket.iter().any(|i| i.address == _new_owner.clone())1845 {1846 let item = basket.iter_mut().find(|i| i.address == _new_owner.clone()).unwrap().clone();1847 let limit_time = item.start_block + ChainLimit::get().fungible_sponsor_transfer_timeout.into();1848 if block_number >= limit_time {1849 basket.retain(|x| x.address == item.address);1850 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone() });1851 <FungibleTransferBasket<T>>::insert(collection_id, _item_id, basket);1852 true1853 }1854 else {1855 false1856 }1857 }1858 else {1859 basket.push(BasketItem { start_block: block_number, address: _new_owner.clone()});1860 true1861 }1862 }1863 CollectionMode::ReFungible(_, _) => {1864 let basket = <ReFungibleTransferBasket<T>>::get(collection_id, _item_id);1865 let block_number = <system::Module<T>>::block_number() as T::BlockNumber;1866 let limit_time = basket + ChainLimit::get().nft_sponsor_transfer_timeout.into();1867 if block_number >= limit_time {1868 <ReFungibleTransferBasket<T>>::insert(collection_id, _item_id, block_number);1869 true1870 } else {1871 false1872 }1873 }1874 _ => {1875 false1876 },1877 };18781879 if !sponsor_transfer {1880 T::AccountId::default()1881 } else {1882 <Collection<T>>::get(collection_id).sponsor1883 }1884 }18851886 _ => T::AccountId::default(),1887 };18881889 let mut who_pays_fee: T::AccountId = sponsor.clone();1890 if sponsor == T::AccountId::default() {1891 who_pays_fee = who.clone();1892 }18931894 // Only mess with balances if fee is not zero.1895 if fee.is_zero() {1896 return Ok((fee, None));1897 }18981899 match <T as transaction_payment::Trait>::Currency::withdraw(1900 &who_pays_fee,1901 fee,1902 if tip.is_zero() {1903 WithdrawReason::TransactionPayment.into()1904 } else {1905 WithdrawReason::TransactionPayment | WithdrawReason::Tip1906 },1907 ExistenceRequirement::KeepAlive,1908 ) {1909 Ok(imbalance) => Ok((fee, Some(imbalance))),1910 Err(_) => Err(InvalidTransaction::Payment.into()),1911 }1912 }1913}19141915impl<T: Trait + transaction_payment::Trait + Send + Sync> SignedExtension1916 for ChargeTransactionPayment<T>1917where1918 BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,1919 T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>>,1920{1921 const IDENTIFIER: &'static str = "ChargeTransactionPayment";1922 type AccountId = T::AccountId;1923 type Call = T::Call;1924 type AdditionalSigned = ();1925 type Pre = (1926 BalanceOf<T>,1927 Self::AccountId,1928 Option<NegativeImbalanceOf<T>>,1929 BalanceOf<T>,1930 );1931 fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {1932 Ok(())1933 }19341935 fn validate(1936 &self,1937 who: &Self::AccountId,1938 call: &Self::Call,1939 info: &DispatchInfoOf<Self::Call>,1940 len: usize,1941 ) -> TransactionValidity {1942 let (fee, _) = self.withdraw_fee(who, call, info, len)?;19431944 let mut r = ValidTransaction::default();1945 // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which1946 // will be a bit more than setting the priority to tip. For now, this is enough.1947 r.priority = fee.saturated_into::<TransactionPriority>();1948 Ok(r)1949 }19501951 fn pre_dispatch(1952 self,1953 who: &Self::AccountId,1954 call: &Self::Call,1955 info: &DispatchInfoOf<Self::Call>,1956 len: usize,1957 ) -> Result<Self::Pre, TransactionValidityError> {1958 let (fee, imbalance) = self.withdraw_fee(who, call, info, len)?;1959 Ok((self.0, who.clone(), imbalance, fee))1960 }19611962 fn post_dispatch(1963 pre: Self::Pre,1964 info: &DispatchInfoOf<Self::Call>,1965 post_info: &PostDispatchInfoOf<Self::Call>,1966 len: usize,1967 _result: &DispatchResult,1968 ) -> Result<(), TransactionValidityError> {1969 let (tip, who, imbalance, fee) = pre;1970 if let Some(payed) = imbalance {1971 let actual_fee = <transaction_payment::Module<T>>::compute_actual_fee(1972 len as u32, info, post_info, tip,1973 );1974 let refund = fee.saturating_sub(actual_fee);1975 let actual_payment =1976 match <T as transaction_payment::Trait>::Currency::deposit_into_existing(1977 &who, refund,1978 ) {1979 Ok(refund_imbalance) => {1980 // The refund cannot be larger than the up front payed max weight.1981 // `PostDispatchInfo::calc_unspent` guards against such a case.1982 match payed.offset(refund_imbalance) {1983 Ok(actual_payment) => actual_payment,1984 Err(_) => return Err(InvalidTransaction::Payment.into()),1985 }1986 }1987 // We do not recreate the account using the refund. The up front payment1988 // is gone in that case.1989 Err(_) => payed,1990 };1991 let imbalances = actual_payment.split(tip);1992 <T as transaction_payment::Trait>::OnTransactionPayment::on_unbalanceds(1993 Some(imbalances.0).into_iter().chain(Some(imbalances.1)),1994 );1995 }1996 Ok(())1997 }1998}1999// #endregion20002001runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -528,16 +528,16 @@
use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};
let whitelist: Vec<TrackedStorageKey> = vec![
- // Block Number
- hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
- // Total Issuance
- hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),
- // Execution Phase
- hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),
- // Event Count
- hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),
- // System Events
- hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),
+ // Alice account
+ hex_literal::hex!("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d").to_vec().into(),
+ // // Total Issuance
+ // hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),
+ // // Execution Phase
+ // hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),
+ // // Event Count
+ // hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),
+ // // System Events
+ // hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),
];
let mut batches = Vec::<BenchmarkBatch>::new();