git.delta.rocks / unique-network / refs/commits / b3a3ee6c7b77

difftreelog

fix use new weights

Yaroslav Bolyukin2021-09-01parent: #87c4019.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
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}
after · pallets/nft/src/lib.rs
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 create_multiple_items(amount: u32) -> Weight {246		Self::create_multiple_items_nft(amount)247			.max(Self::create_multiple_items_fungible(amount))248			.max(Self::create_multiple_items_refungible(amount))249	}250	fn burn_item() -> Weight {251		// TODO: refungible, fungible252		Self::burn_item_nft()253	}254}255impl<T: WeightInfo> WeightInfoHelpers for T {}256257// # Used definitions258//259// ## User control levels260//261// chain-controlled - key is uncontrolled by user262//                    i.e autoincrementing index263//                    can use non-cryptographic hash264// real - key is controlled by user265//        but it is hard to generate enough colliding values, i.e owner of signed txs266//        can use non-cryptographic hash267// controlled - key is completly controlled by users268//              i.e maps with mutable keys269//              should use cryptographic hash270//271// ## User control level downgrade reasons272//273// ?1 - chain-controlled -> controlled274//      collections/tokens can be destroyed, resulting in massive holes275// ?2 - chain-controlled -> controlled276//      same as ?1, but can be only added, resulting in easier exploitation277// ?3 - real -> controlled278//      no confirmation required, so addresses can be easily generated279decl_storage! {280	trait Store for Module<T: Config> as Nft {281282		//#region Private members283		/// Id of next collection284		CreatedCollectionCount: u32;285		/// Used for migrations286		ChainVersion: u64;287		/// Id of last collection token288		/// Collection id (controlled?1)289		ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;290		//#endregion291292		//#region Bound counters293		/// Amount of collections destroyed, used for total amount tracking with294		/// CreatedCollectionCount295		DestroyedCollectionCount: u32;296		/// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)297		/// Account id (real)298		pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;299		//#endregion300301		//#region Basic collections302		/// Collection info303		/// Collection id (controlled?1)304		pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;305		/// List of collection admins306		/// Collection id (controlled?2)307		pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;308		/// Whitelisted collection users309		/// Collection id (controlled?2), user id (controlled?3)310		pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;311		//#endregion312313		/// How many of collection items user have314		/// Collection id (controlled?2), account id (real)315		pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;316317		/// Amount of items which spender can transfer out of owners account (via transferFrom)318		/// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))319		/// TODO: Off chain worker should remove from this map when token gets removed320		pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;321322		//#region Item collections323		/// Collection id (controlled?2), token id (controlled?1)324		pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;325		/// Collection id (controlled?2), owner (controlled?2)326		pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;327		/// Collection id (controlled?2), token id (controlled?1)328		pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;329		//#endregion330331		//#region Index list332		/// Collection id (controlled?2), tokens owner (controlled?2)333		pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;334		//#endregion335336		//#region Tokens transfer rate limit baskets337		/// (Collection id (controlled?2), who created (real))338		/// TODO: Off chain worker should remove from this map when collection gets removed339		pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;340		/// Collection id (controlled?2), token id (controlled?2)341		pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;342		/// Collection id (controlled?2), owning user (real)343		pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;344		/// Collection id (controlled?2), token id (controlled?2)345		pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;346		//#endregion347348		/// Variable metadata sponsoring349		/// Collection id (controlled?2), token id (controlled?2)350		pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;351	}352	add_extra_genesis {353		build(|config: &GenesisConfig<T>| {354			// Modification of storage355			for (_num, _c) in &config.collection_id {356				<Module<T>>::init_collection(_c);357			}358359			for (_num, _c, _i) in &config.nft_item_id {360				<Module<T>>::init_nft_token(*_c, _i);361			}362363			for (collection_id, account_id, fungible_item) in &config.fungible_item_id {364				<Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);365			}366367			for (_num, _c, _i) in &config.refungible_item_id {368				<Module<T>>::init_refungible_token(*_c, _i);369			}370		})371	}372}373374decl_event!(375	pub enum Event<T>376	where377		AccountId = <T as frame_system::Config>::AccountId,378		CrossAccountId = <T as Config>::CrossAccountId,379	{380		/// New collection was created381		///382		/// # Arguments383		///384		/// * collection_id: Globally unique identifier of newly created collection.385		///386		/// * mode: [CollectionMode] converted into u8.387		///388		/// * account_id: Collection owner.389		CollectionCreated(CollectionId, u8, AccountId),390391		/// New item was created.392		///393		/// # Arguments394		///395		/// * collection_id: Id of the collection where item was created.396		///397		/// * item_id: Id of an item. Unique within the collection.398		///399		/// * recipient: Owner of newly created item400		ItemCreated(CollectionId, TokenId, CrossAccountId),401402		/// Collection item was burned.403		///404		/// # Arguments405		///406		/// collection_id.407		///408		/// item_id: Identifier of burned NFT.409		ItemDestroyed(CollectionId, TokenId),410411		/// Item was transferred412		///413		/// * collection_id: Id of collection to which item is belong414		///415		/// * item_id: Id of an item416		///417		/// * sender: Original owner of item418		///419		/// * recipient: New owner of item420		///421		/// * amount: Always 1 for NFT422		Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),423424		/// * collection_id425		///426		/// * item_id427		///428		/// * sender429		///430		/// * spender431		///432		/// * amount433		Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),434	}435);436437decl_module! {438	pub struct Module<T: Config> for enum Call439	where440		origin: T::Origin441	{442		fn deposit_event() = default;443		const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;444		type Error = Error<T>;445446		fn on_initialize(_now: T::BlockNumber) -> Weight {447			0448		}449450		/// 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.451		///452		/// # Permissions453		///454		/// * Anyone.455		///456		/// # Arguments457		///458		/// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.459		///460		/// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.461		///462		/// * token_prefix: UTF-8 string with token prefix.463		///464		/// * mode: [CollectionMode] collection type and type dependent data.465		// returns collection ID466		#[weight = <SelfWeightOf<T>>::create_collection()]467		#[transactional]468		pub fn create_collection(origin,469								 collection_name: Vec<u16>,470								 collection_description: Vec<u16>,471								 token_prefix: Vec<u8>,472								 mode: CollectionMode) -> DispatchResult {473474			// Anyone can create a collection475			let who = ensure_signed(origin)?;476477			// Take a (non-refundable) deposit of collection creation478			let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();479			imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(480				&T::TreasuryAccountId::get(),481				T::CollectionCreationPrice::get(),482			));483			<T as Config>::Currency::settle(484				&who,485				imbalance,486				WithdrawReasons::TRANSFER,487				ExistenceRequirement::KeepAlive,488			).map_err(|_| Error::<T>::NoPermission)?;489490			let decimal_points = match mode {491				CollectionMode::Fungible(points) => points,492				_ => 0493			};494495			let created_count = CreatedCollectionCount::get();496			let destroyed_count = DestroyedCollectionCount::get();497498			// bound Total number of collections499			ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);500501			// check params502			ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);503			ensure!(collection_name.len() <= MAX_COLLECTION_NAME_LENGTH, Error::<T>::CollectionNameLimitExceeded);504			ensure!(collection_description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH, Error::<T>::CollectionDescriptionLimitExceeded);505			ensure!(token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH, Error::<T>::CollectionTokenPrefixLimitExceeded);506507			// Generate next collection ID508			let next_id = created_count509				.checked_add(1)510				.ok_or(Error::<T>::NumOverflow)?;511512			CreatedCollectionCount::put(next_id);513514			let limits = CollectionLimits {515				sponsored_data_size: CUSTOM_DATA_LIMIT,516				..Default::default()517			};518519			// Create new collection520			let new_collection = Collection {521				owner: who.clone(),522				name: collection_name,523				mode: mode.clone(),524				mint_mode: false,525				access: AccessMode::Normal,526				description: collection_description,527				decimal_points,528				token_prefix,529				offchain_schema: Vec::new(),530				schema_version: SchemaVersion::ImageURL,531				sponsorship: SponsorshipState::Disabled,532				variable_on_chain_schema: Vec::new(),533				const_on_chain_schema: Vec::new(),534				limits,535				transfers_enabled: true,536			};537538			// Add new collection to map539			<CollectionById<T>>::insert(next_id, new_collection);540541			// call event542			Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));543544			Ok(())545		}546547		/// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.548		///549		/// # Permissions550		///551		/// * Collection Owner.552		///553		/// # Arguments554		///555		/// * collection_id: collection to destroy.556		#[weight = <SelfWeightOf<T>>::destroy_collection()]557		#[transactional]558		pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {559560			let sender = ensure_signed(origin)?;561			let collection = Self::get_collection(collection_id)?;562			Self::check_owner_permissions(&collection, &sender)?;563			if !collection.limits.owner_can_destroy {564				fail!(Error::<T>::NoPermission);565			}566567			<AddressTokens<T>>::remove_prefix(collection_id, None);568			<Allowances<T>>::remove_prefix(collection_id, None);569			<Balance<T>>::remove_prefix(collection_id, None);570			<ItemListIndex>::remove(collection_id);571			<AdminList<T>>::remove(collection_id);572			<CollectionById<T>>::remove(collection_id);573			<WhiteList<T>>::remove_prefix(collection_id, None);574575			<NftItemList<T>>::remove_prefix(collection_id, None);576			<FungibleItemList<T>>::remove_prefix(collection_id, None);577			<ReFungibleItemList<T>>::remove_prefix(collection_id, None);578579			<NftTransferBasket<T>>::remove_prefix(collection_id, None);580			<FungibleTransferBasket<T>>::remove_prefix(collection_id, None);581			<ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);582583			<VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);584585			DestroyedCollectionCount::put(DestroyedCollectionCount::get()586				.checked_add(1)587				.ok_or(Error::<T>::NumOverflow)?);588589			Ok(())590		}591592		/// Add an address to white list.593		///594		/// # Permissions595		///596		/// * Collection Owner597		/// * Collection Admin598		///599		/// # Arguments600		///601		/// * collection_id.602		///603		/// * address.604		#[weight = <SelfWeightOf<T>>::add_to_white_list()]605		#[transactional]606		pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{607608			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);609			let collection = Self::get_collection(collection_id)?;610611			Self::toggle_white_list_internal(612				&sender,613				&collection,614				&address,615				true,616			)?;617618			Ok(())619		}620621		/// Remove an address from white list.622		///623		/// # Permissions624		///625		/// * Collection Owner626		/// * Collection Admin627		///628		/// # Arguments629		///630		/// * collection_id.631		///632		/// * address.633		#[weight = <SelfWeightOf<T>>::remove_from_white_list()]634		#[transactional]635		pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{636637			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);638			let collection = Self::get_collection(collection_id)?;639640			Self::toggle_white_list_internal(641				&sender,642				&collection,643				&address,644				false,645			)?;646647			Ok(())648		}649650		/// Toggle between normal and white list access for the methods with access for `Anyone`.651		///652		/// # Permissions653		///654		/// * Collection Owner.655		///656		/// # Arguments657		///658		/// * collection_id.659		///660		/// * mode: [AccessMode]661		#[weight = <SelfWeightOf<T>>::set_public_access_mode()]662		#[transactional]663		pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult664		{665			let sender = ensure_signed(origin)?;666667			let mut target_collection = Self::get_collection(collection_id)?;668			Self::check_owner_permissions(&target_collection, &sender)?;669			target_collection.access = mode;670			target_collection.save()671		}672673		/// Allows Anyone to create tokens if:674		/// * White List is enabled, and675		/// * Address is added to white list, and676		/// * This method was called with True parameter677		///678		/// # Permissions679		/// * Collection Owner680		///681		/// # Arguments682		///683		/// * collection_id.684		///685		/// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.686		#[weight = <SelfWeightOf<T>>::set_mint_permission()]687		#[transactional]688		pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult689		{690			let sender = ensure_signed(origin)?;691692			let mut target_collection = Self::get_collection(collection_id)?;693			Self::check_owner_permissions(&target_collection, &sender)?;694			target_collection.mint_mode = mint_permission;695			target_collection.save()696		}697698		/// Change the owner of the collection.699		///700		/// # Permissions701		///702		/// * Collection Owner.703		///704		/// # Arguments705		///706		/// * collection_id.707		///708		/// * new_owner.709		#[weight = <SelfWeightOf<T>>::change_collection_owner()]710		#[transactional]711		pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {712713			let sender = ensure_signed(origin)?;714			let mut target_collection = Self::get_collection(collection_id)?;715			Self::check_owner_permissions(&target_collection, &sender)?;716			target_collection.owner = new_owner;717			target_collection.save()718		}719720		/// Adds an admin of the Collection.721		/// 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.722		///723		/// # Permissions724		///725		/// * Collection Owner.726		/// * Collection Admin.727		///728		/// # Arguments729		///730		/// * collection_id: ID of the Collection to add admin for.731		///732		/// * new_admin_id: Address of new admin to add.733		#[weight = <SelfWeightOf<T>>::add_collection_admin()]734		#[transactional]735		pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {736			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);737			let collection = Self::get_collection(collection_id)?;738			Self::check_owner_or_admin_permissions(&collection, &sender)?;739			let mut admin_arr = <AdminList<T>>::get(collection_id);740741			match admin_arr.binary_search(&new_admin_id) {742				Ok(_) => {},743				Err(idx) => {744					ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);745					admin_arr.insert(idx, new_admin_id);746					<AdminList<T>>::insert(collection_id, admin_arr);747				}748			}749			Ok(())750		}751752		/// 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.753		///754		/// # Permissions755		///756		/// * Collection Owner.757		/// * Collection Admin.758		///759		/// # Arguments760		///761		/// * collection_id: ID of the Collection to remove admin for.762		///763		/// * account_id: Address of admin to remove.764		#[weight = <SelfWeightOf<T>>::remove_collection_admin()]765		#[transactional]766		pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {767			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);768			let collection = Self::get_collection(collection_id)?;769			Self::check_owner_or_admin_permissions(&collection, &sender)?;770			let mut admin_arr = <AdminList<T>>::get(collection_id);771772			if let Ok(idx) = admin_arr.binary_search(&account_id) {773				admin_arr.remove(idx);774				<AdminList<T>>::insert(collection_id, admin_arr);775			}776			Ok(())777		}778779		/// # Permissions780		///781		/// * Collection Owner782		///783		/// # Arguments784		///785		/// * collection_id.786		///787		/// * new_sponsor.788		#[weight = <SelfWeightOf<T>>::set_collection_sponsor()]789		#[transactional]790		pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {791			let sender = ensure_signed(origin)?;792			let mut target_collection = Self::get_collection(collection_id)?;793			Self::check_owner_permissions(&target_collection, &sender)?;794795			target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);796			target_collection.save()797		}798799		/// # Permissions800		///801		/// * Sponsor.802		///803		/// # Arguments804		///805		/// * collection_id.806		#[weight = <SelfWeightOf<T>>::confirm_sponsorship()]807		#[transactional]808		pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {809			let sender = ensure_signed(origin)?;810811			let mut target_collection = Self::get_collection(collection_id)?;812			ensure!(813				target_collection.sponsorship.pending_sponsor() == Some(&sender),814				Error::<T>::ConfirmUnsetSponsorFail815			);816817			target_collection.sponsorship = SponsorshipState::Confirmed(sender);818			target_collection.save()819		}820821		/// Switch back to pay-per-own-transaction model.822		///823		/// # Permissions824		///825		/// * Collection owner.826		///827		/// # Arguments828		///829		/// * collection_id.830		#[weight = <SelfWeightOf<T>>::remove_collection_sponsor()]831		#[transactional]832		pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {833			let sender = ensure_signed(origin)?;834835			let mut target_collection = Self::get_collection(collection_id)?;836			Self::check_owner_permissions(&target_collection, &sender)?;837838			target_collection.sponsorship = SponsorshipState::Disabled;839			target_collection.save()840		}841842		/// This method creates a concrete instance of NFT Collection created with CreateCollection method.843		///844		/// # Permissions845		///846		/// * Collection Owner.847		/// * Collection Admin.848		/// * Anyone if849		///     * White List is enabled, and850		///     * Address is added to white list, and851		///     * MintPermission is enabled (see SetMintPermission method)852		///853		/// # Arguments854		///855		/// * collection_id: ID of the collection.856		///857		/// * owner: Address, initial owner of the NFT.858		///859		/// * data: Token data to store on chain.860		// #[weight =861		// (130_000_000 as Weight)862		// .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))863		// .saturating_add(RocksDbWeight::get().reads(10 as Weight))864		// .saturating_add(RocksDbWeight::get().writes(8 as Weight))]865866		#[weight = <SelfWeightOf<T>>::create_item(data.data_size() as u32)]867		#[transactional]868		pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {869			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);870			let collection = Self::get_collection(collection_id)?;871872			Self::create_item_internal(&sender, &collection, &owner, data)?;873874			collection.submit_logs()875		}876877		/// This method creates multiple items in a collection created with CreateCollection method.878		///879		/// # Permissions880		///881		/// * Collection Owner.882		/// * Collection Admin.883		/// * Anyone if884		///     * White List is enabled, and885		///     * Address is added to white list, and886		///     * MintPermission is enabled (see SetMintPermission method)887		///888		/// # Arguments889		///890		/// * collection_id: ID of the collection.891		///892		/// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].893		///894		/// * owner: Address, initial owner of the NFT.895		#[weight = <SelfWeightOf<T>>::create_multiple_items(items_data.len() as u32)]896		#[transactional]897		pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {898899			ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);900			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);901			let collection = Self::get_collection(collection_id)?;902903			Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;904905			collection.submit_logs()906		}907908		// TODO! transaction weight909910		/// Set transfers_enabled value for particular collection911		///912		/// # Permissions913		///914		/// * Collection Owner.915		///916		/// # Arguments917		///918		/// * collection_id: ID of the collection.919		///920		/// * value: New flag value.921		#[weight = <SelfWeightOf<T>>::set_transfers_enabled_flag()]922		#[transactional]923		pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {924925			let sender = ensure_signed(origin)?;926			let mut target_collection = Self::get_collection(collection_id)?;927928			Self::check_owner_permissions(&target_collection, &sender)?;929930			target_collection.transfers_enabled = value;931			target_collection.save()932		}933934		/// Destroys a concrete instance of NFT.935		///936		/// # Permissions937		///938		/// * Collection Owner.939		/// * Collection Admin.940		/// * Current NFT Owner.941		///942		/// # Arguments943		///944		/// * collection_id: ID of the collection.945		///946		/// * item_id: ID of NFT to burn.947		#[weight = <SelfWeightOf<T>>::burn_item()]948		#[transactional]949		pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {950951			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952			let target_collection = Self::get_collection(collection_id)?;953954			Self::burn_item_internal(&sender, &target_collection, item_id, value)?;955956			target_collection.submit_logs()957		}958959		/// Change ownership of the token.960		///961		/// # Permissions962		///963		/// * Collection Owner964		/// * Collection Admin965		/// * Current NFT owner966		///967		/// # Arguments968		///969		/// * recipient: Address of token recipient.970		///971		/// * collection_id.972		///973		/// * item_id: ID of the item974		///     * Non-Fungible Mode: Required.975		///     * Fungible Mode: Ignored.976		///     * Re-Fungible Mode: Required.977		///978		/// * value: Amount to transfer.979		///     * Non-Fungible Mode: Ignored980		///     * Fungible Mode: Must specify transferred amount981		///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)982		#[weight = <SelfWeightOf<T>>::transfer()]983		#[transactional]984		pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {985			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);986			let collection = Self::get_collection(collection_id)?;987988			Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;989990			collection.submit_logs()991		}992993		/// Set, change, or remove approved address to transfer the ownership of the NFT.994		///995		/// # Permissions996		///997		/// * Collection Owner998		/// * Collection Admin999		/// * Current NFT owner1000		///1001		/// # Arguments1002		///1003		/// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1004		///1005		/// * collection_id.1006		///1007		/// * item_id: ID of the item.1008		#[weight = <SelfWeightOf<T>>::approve()]1009		#[transactional]1010		pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1011			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1012			let collection = Self::get_collection(collection_id)?;10131014			Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10151016			collection.submit_logs()1017		}10181019		/// 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.1020		///1021		/// # Permissions1022		/// * Collection Owner1023		/// * Collection Admin1024		/// * Current NFT owner1025		/// * Address approved by current NFT owner1026		///1027		/// # Arguments1028		///1029		/// * from: Address that owns token.1030		///1031		/// * recipient: Address of token recipient.1032		///1033		/// * collection_id.1034		///1035		/// * item_id: ID of the item.1036		///1037		/// * value: Amount to transfer.1038		#[weight = <SelfWeightOf<T>>::transfer_from()]1039		#[transactional]1040		pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1041			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1042			let collection = Self::get_collection(collection_id)?;10431044			Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10451046			collection.submit_logs()1047		}1048		// #[weight = 0]1049		//     // let no_perm_mes = "You do not have permissions to modify this collection";1050		//     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1051		//     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1052		//     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10531054		//     // // on_nft_received  call10551056		//     // Self::transfer(origin, collection_id, item_id, new_owner)?;10571058		//     Ok(())1059		// }10601061		/// Set off-chain data schema.1062		///1063		/// # Permissions1064		///1065		/// * Collection Owner1066		/// * Collection Admin1067		///1068		/// # Arguments1069		///1070		/// * collection_id.1071		///1072		/// * schema: String representing the offchain data schema.1073		#[weight = <SelfWeightOf<T>>::set_variable_meta_data(data.len() as u32)]1074		#[transactional]1075		pub fn set_variable_meta_data (1076			origin,1077			collection_id: CollectionId,1078			item_id: TokenId,1079			data: Vec<u8>1080		) -> DispatchResult {1081			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10821083			let collection = Self::get_collection(collection_id)?;10841085			Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10861087			Ok(())1088		}10891090		/// Set schema standard1091		/// ImageURL1092		/// Unique1093		///1094		/// # Permissions1095		///1096		/// * Collection Owner1097		/// * Collection Admin1098		///1099		/// # Arguments1100		///1101		/// * collection_id.1102		///1103		/// * schema: SchemaVersion: enum1104		#[weight = <SelfWeightOf<T>>::set_schema_version()]1105		#[transactional]1106		pub fn set_schema_version(1107			origin,1108			collection_id: CollectionId,1109			version: SchemaVersion1110		) -> DispatchResult {1111			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1112			let mut target_collection = Self::get_collection(collection_id)?;1113			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1114			target_collection.schema_version = version;1115			target_collection.save()1116		}11171118		/// Set off-chain data schema.1119		///1120		/// # Permissions1121		///1122		/// * Collection Owner1123		/// * Collection Admin1124		///1125		/// # Arguments1126		///1127		/// * collection_id.1128		///1129		/// * schema: String representing the offchain data schema.1130		#[weight = <SelfWeightOf<T>>::set_offchain_schema(schema.len() as u32)]1131		#[transactional]1132		pub fn set_offchain_schema(1133			origin,1134			collection_id: CollectionId,1135			schema: Vec<u8>1136		) -> DispatchResult {1137			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1138			let mut target_collection = Self::get_collection(collection_id)?;1139			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11401141			// check schema limit1142			ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11431144			target_collection.offchain_schema = schema;1145			target_collection.save()1146		}11471148		/// Set const on-chain data schema.1149		///1150		/// # Permissions1151		///1152		/// * Collection Owner1153		/// * Collection Admin1154		///1155		/// # Arguments1156		///1157		/// * collection_id.1158		///1159		/// * schema: String representing the const on-chain data schema.1160		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1161		#[transactional]1162		pub fn set_const_on_chain_schema (1163			origin,1164			collection_id: CollectionId,1165			schema: Vec<u8>1166		) -> DispatchResult {1167			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1168			let mut target_collection = Self::get_collection(collection_id)?;1169			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11701171			// check schema limit1172			ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11731174			target_collection.const_on_chain_schema = schema;1175			target_collection.save()1176		}11771178		/// Set variable on-chain data schema.1179		///1180		/// # Permissions1181		///1182		/// * Collection Owner1183		/// * Collection Admin1184		///1185		/// # Arguments1186		///1187		/// * collection_id.1188		///1189		/// * schema: String representing the variable on-chain data schema.1190		#[weight = <SelfWeightOf<T>>::set_const_on_chain_schema(schema.len() as u32)]1191		#[transactional]1192		pub fn set_variable_on_chain_schema (1193			origin,1194			collection_id: CollectionId,1195			schema: Vec<u8>1196		) -> DispatchResult {1197			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1198			let mut target_collection = Self::get_collection(collection_id)?;1199			Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12001201			// check schema limit1202			ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");12031204			target_collection.variable_on_chain_schema = schema;1205			target_collection.save()1206		}12071208		#[weight = <SelfWeightOf<T>>::set_collection_limits()]1209		#[transactional]1210		pub fn set_collection_limits(1211			origin,1212			collection_id: u32,1213			new_limits: CollectionLimits<T::BlockNumber>,1214		) -> DispatchResult {1215			let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1216			let mut target_collection = Self::get_collection(collection_id)?;1217			Self::check_owner_permissions(&target_collection, sender.as_sub())?;1218			let old_limits = &target_collection.limits;12191220			// collection bounds1221			ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1222				new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1223				new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1224				Error::<T>::CollectionLimitBoundsExceeded);12251226			// token_limit   check  prev1227			ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1228			ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12291230			ensure!(1231				(old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1232				(old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1233				Error::<T>::OwnerPermissionsCantBeReverted,1234			);12351236			target_collection.limits = new_limits;12371238			target_collection.save()1239		}1240	}1241}12421243impl<T: Config> Module<T> {1244	pub fn create_item_internal(1245		sender: &T::CrossAccountId,1246		collection: &CollectionHandle<T>,1247		owner: &T::CrossAccountId,1248		data: CreateItemData,1249	) -> DispatchResult {1250		Self::can_create_items_in_collection(collection, sender, owner, 1)?;1251		Self::validate_create_item_args(collection, &data)?;1252		Self::create_item_no_validation(collection, owner, data)?;12531254		Ok(())1255	}12561257	pub fn transfer_internal(1258		sender: &T::CrossAccountId,1259		recipient: &T::CrossAccountId,1260		target_collection: &CollectionHandle<T>,1261		item_id: TokenId,1262		value: u128,1263	) -> DispatchResult {1264		target_collection.consume_gas(2000000)?;1265		// Limits check1266		Self::is_correct_transfer(target_collection, recipient)?;12671268		// Transfer permissions check1269		ensure!(1270			Self::is_item_owner(sender, target_collection, item_id)1271				|| Self::is_owner_or_admin_permissions(target_collection, sender),1272			Error::<T>::NoPermission1273		);12741275		if target_collection.access == AccessMode::WhiteList {1276			Self::check_white_list(target_collection, sender)?;1277			Self::check_white_list(target_collection, recipient)?;1278		}12791280		match target_collection.mode {1281			CollectionMode::NFT => Self::transfer_nft(1282				target_collection,1283				item_id,1284				sender.clone(),1285				recipient.clone(),1286			)?,1287			CollectionMode::Fungible(_) => {1288				Self::transfer_fungible(target_collection, value, sender, recipient)?1289			}1290			CollectionMode::ReFungible => Self::transfer_refungible(1291				target_collection,1292				item_id,1293				value,1294				sender.clone(),1295				recipient.clone(),1296			)?,1297			_ => (),1298		};12991300		Self::deposit_event(RawEvent::Transfer(1301			target_collection.id,1302			item_id,1303			sender.clone(),1304			recipient.clone(),1305			value,1306		));13071308		Ok(())1309	}13101311	pub fn approve_internal(1312		sender: &T::CrossAccountId,1313		spender: &T::CrossAccountId,1314		collection: &CollectionHandle<T>,1315		item_id: TokenId,1316		amount: u128,1317	) -> DispatchResult {1318		collection.consume_gas(2000000)?;1319		Self::token_exists(collection, item_id)?;13201321		// Transfer permissions check1322		let bypasses_limits = collection.limits.owner_can_transfer1323			&& Self::is_owner_or_admin_permissions(collection, sender);13241325		let allowance_limit = if bypasses_limits {1326			None1327		} else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1328			Some(amount)1329		} else {1330			fail!(Error::<T>::NoPermission);1331		};13321333		if collection.access == AccessMode::WhiteList {1334			Self::check_white_list(collection, sender)?;1335			Self::check_white_list(collection, spender)?;1336		}13371338		let allowance: u128 = amount1339			.checked_add(<Allowances<T>>::get(1340				collection.id,1341				(item_id, sender.as_sub(), spender.as_sub()),1342			))1343			.ok_or(Error::<T>::NumOverflow)?;1344		if let Some(limit) = allowance_limit {1345			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1346		}1347		<Allowances<T>>::insert(1348			collection.id,1349			(item_id, sender.as_sub(), spender.as_sub()),1350			allowance,1351		);13521353		if matches!(collection.mode, CollectionMode::NFT) {1354			// TODO: NFT: only one owner may exist for token in ERC7211355			collection.log(ERC721Events::Approval {1356				owner: *sender.as_eth(),1357				approved: *spender.as_eth(),1358				token_id: item_id.into(),1359			})?;1360		}13611362		if matches!(collection.mode, CollectionMode::Fungible(_)) {1363			// TODO: NFT: only one owner may exist for token in ERC201364			collection.log(ERC20Events::Approval {1365				owner: *sender.as_eth(),1366				spender: *spender.as_eth(),1367				value: allowance.into(),1368			})?;1369		}13701371		Self::deposit_event(RawEvent::Approved(1372			collection.id,1373			item_id,1374			sender.clone(),1375			spender.clone(),1376			allowance,1377		));1378		Ok(())1379	}13801381	pub fn transfer_from_internal(1382		sender: &T::CrossAccountId,1383		from: &T::CrossAccountId,1384		recipient: &T::CrossAccountId,1385		collection: &CollectionHandle<T>,1386		item_id: TokenId,1387		amount: u128,1388	) -> DispatchResult {1389		collection.consume_gas(2000000)?;1390		// Check approval1391		let approval: u128 =1392			<Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13931394		// Limits check1395		Self::is_correct_transfer(collection, recipient)?;13961397		// Transfer permissions check1398		ensure!(1399			approval >= amount1400				|| (collection.limits.owner_can_transfer1401					&& Self::is_owner_or_admin_permissions(collection, sender)),1402			Error::<T>::NoPermission1403		);14041405		if collection.access == AccessMode::WhiteList {1406			Self::check_white_list(collection, sender)?;1407			Self::check_white_list(collection, recipient)?;1408		}14091410		// Reduce approval by transferred amount or remove if remaining approval drops to 01411		let allowance = approval.saturating_sub(amount);1412		if allowance > 0 {1413			<Allowances<T>>::insert(1414				collection.id,1415				(item_id, from.as_sub(), sender.as_sub()),1416				allowance,1417			);1418		} else {1419			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1420		}14211422		match collection.mode {1423			CollectionMode::NFT => {1424				Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1425			}1426			CollectionMode::Fungible(_) => {1427				Self::transfer_fungible(collection, amount, from, recipient)?1428			}1429			CollectionMode::ReFungible => Self::transfer_refungible(1430				collection,1431				item_id,1432				amount,1433				from.clone(),1434				recipient.clone(),1435			)?,1436			_ => (),1437		};14381439		if matches!(collection.mode, CollectionMode::Fungible(_)) {1440			collection.log(ERC20Events::Approval {1441				owner: *from.as_eth(),1442				spender: *sender.as_eth(),1443				value: allowance.into(),1444			})?;1445		}14461447		Ok(())1448	}14491450	pub fn set_variable_meta_data_internal(1451		sender: &T::CrossAccountId,1452		collection: &CollectionHandle<T>,1453		item_id: TokenId,1454		data: Vec<u8>,1455	) -> DispatchResult {1456		Self::token_exists(collection, item_id)?;14571458		ensure!(1459			CUSTOM_DATA_LIMIT >= data.len() as u32,1460			Error::<T>::TokenVariableDataLimitExceeded1461		);14621463		// Modify permissions check1464		ensure!(1465			Self::is_item_owner(sender, collection, item_id)1466				|| Self::is_owner_or_admin_permissions(collection, sender),1467			Error::<T>::NoPermission1468		);14691470		match collection.mode {1471			CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1472			CollectionMode::ReFungible => {1473				Self::set_re_fungible_variable_data(collection, item_id, data)?1474			}1475			CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1476			_ => fail!(Error::<T>::UnexpectedCollectionType),1477		};14781479		Ok(())1480	}14811482	pub fn create_multiple_items_internal(1483		sender: &T::CrossAccountId,1484		collection: &CollectionHandle<T>,1485		owner: &T::CrossAccountId,1486		items_data: Vec<CreateItemData>,1487	) -> DispatchResult {1488		Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14891490		for data in &items_data {1491			Self::validate_create_item_args(collection, data)?;1492		}1493		for data in &items_data {1494			Self::create_item_no_validation(collection, owner, data.clone())?;1495		}14961497		Ok(())1498	}14991500	pub fn burn_item_internal(1501		sender: &T::CrossAccountId,1502		collection: &CollectionHandle<T>,1503		item_id: TokenId,1504		value: u128,1505	) -> DispatchResult {1506		ensure!(1507			Self::is_item_owner(sender, collection, item_id)1508				|| (collection.limits.owner_can_transfer1509					&& Self::is_owner_or_admin_permissions(collection, sender)),1510			Error::<T>::NoPermission1511		);15121513		if collection.access == AccessMode::WhiteList {1514			Self::check_white_list(collection, sender)?;1515		}15161517		match collection.mode {1518			CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1519			CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1520			CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1521			_ => (),1522		};15231524		Ok(())1525	}15261527	pub fn toggle_white_list_internal(1528		sender: &T::CrossAccountId,1529		collection: &CollectionHandle<T>,1530		address: &T::CrossAccountId,1531		whitelisted: bool,1532	) -> DispatchResult {1533		Self::check_owner_or_admin_permissions(collection, sender)?;15341535		if whitelisted {1536			<WhiteList<T>>::insert(collection.id, address.as_sub(), true);1537		} else {1538			<WhiteList<T>>::remove(collection.id, address.as_sub());1539		}15401541		Ok(())1542	}15431544	fn is_correct_transfer(1545		collection: &CollectionHandle<T>,1546		recipient: &T::CrossAccountId,1547	) -> DispatchResult {1548		let collection_id = collection.id;15491550		// check token limit and account token limit1551		let account_items: u32 =1552			<AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1553		ensure!(1554			collection.limits.account_token_ownership_limit > account_items,1555			Error::<T>::AccountTokenLimitExceeded1556		);15571558		// preliminary transfer check1559		ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15601561		Ok(())1562	}15631564	fn can_create_items_in_collection(1565		collection: &CollectionHandle<T>,1566		sender: &T::CrossAccountId,1567		owner: &T::CrossAccountId,1568		amount: u32,1569	) -> DispatchResult {1570		let collection_id = collection.id;15711572		// check token limit and account token limit1573		let total_items: u32 = ItemListIndex::get(collection_id)1574			.checked_add(amount)1575			.ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1576		let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1577			as u32)1578			.checked_add(amount)1579			.ok_or(Error::<T>::AccountTokenLimitExceeded)?;1580		ensure!(1581			collection.limits.token_limit >= total_items,1582			Error::<T>::CollectionTokenLimitExceeded1583		);1584		ensure!(1585			collection.limits.account_token_ownership_limit >= account_items,1586			Error::<T>::AccountTokenLimitExceeded1587		);15881589		if !Self::is_owner_or_admin_permissions(collection, sender) {1590			ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1591			Self::check_white_list(collection, owner)?;1592			Self::check_white_list(collection, sender)?;1593		}15941595		Ok(())1596	}15971598	fn validate_create_item_args(1599		target_collection: &CollectionHandle<T>,1600		data: &CreateItemData,1601	) -> DispatchResult {1602		match target_collection.mode {1603			CollectionMode::NFT => {1604				if !matches!(data, CreateItemData::NFT(_)) {1605					fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1606				}1607			}1608			CollectionMode::Fungible(_) => {1609				if !matches!(data, CreateItemData::Fungible(_)) {1610					fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1611				}1612			}1613			CollectionMode::ReFungible => {1614				if let CreateItemData::ReFungible(data) = data {1615					// Check refungibility limits1616					ensure!(1617						data.pieces <= MAX_REFUNGIBLE_PIECES,1618						Error::<T>::WrongRefungiblePieces1619					);1620					ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1621				} else {1622					fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1623				}1624			}1625			_ => {1626				fail!(Error::<T>::UnexpectedCollectionType);1627			}1628		};16291630		Ok(())1631	}16321633	fn create_item_no_validation(1634		collection: &CollectionHandle<T>,1635		owner: &T::CrossAccountId,1636		data: CreateItemData,1637	) -> DispatchResult {1638		match data {1639			CreateItemData::NFT(data) => {1640				let item = NftItemType {1641					owner: owner.clone(),1642					const_data: data.const_data.into_inner(),1643					variable_data: data.variable_data.into_inner(),1644				};16451646				Self::add_nft_item(collection, item)?;1647			}1648			CreateItemData::Fungible(data) => {1649				Self::add_fungible_item(collection, owner, data.value)?;1650			}1651			CreateItemData::ReFungible(data) => {1652				let owner_list = vec![Ownership {1653					owner: owner.clone(),1654					fraction: data.pieces,1655				}];16561657				let item = ReFungibleItemType {1658					owner: owner_list,1659					const_data: data.const_data.into_inner(),1660					variable_data: data.variable_data.into_inner(),1661				};16621663				Self::add_refungible_item(collection, item)?;1664			}1665		};16661667		Ok(())1668	}16691670	fn add_fungible_item(1671		collection: &CollectionHandle<T>,1672		owner: &T::CrossAccountId,1673		value: u128,1674	) -> DispatchResult {1675		let collection_id = collection.id;16761677		// Does new owner already have an account?1678		let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16791680		// Mint1681		let item = FungibleItemType {1682			value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1683		};1684		<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16851686		// Update balance1687		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1688			.checked_add(value)1689			.ok_or(Error::<T>::NumOverflow)?;1690		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16911692		Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1693		Ok(())1694	}16951696	fn add_refungible_item(1697		collection: &CollectionHandle<T>,1698		item: ReFungibleItemType<T::CrossAccountId>,1699	) -> DispatchResult {1700		let collection_id = collection.id;17011702		let current_index = <ItemListIndex>::get(collection_id)1703			.checked_add(1)1704			.ok_or(Error::<T>::NumOverflow)?;1705		let itemcopy = item.clone();17061707		ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1708		let item_owner = item.owner.first().expect("only one owner is defined");17091710		let value = item_owner.fraction;1711		let owner = item_owner.owner.clone();17121713		Self::add_token_index(collection_id, current_index, &owner)?;17141715		<ItemListIndex>::insert(collection_id, current_index);1716		<ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17171718		// Update balance1719		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1720			.checked_add(value)1721			.ok_or(Error::<T>::NumOverflow)?;1722		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17231724		Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1725		Ok(())1726	}17271728	fn add_nft_item(1729		collection: &CollectionHandle<T>,1730		item: NftItemType<T::CrossAccountId>,1731	) -> DispatchResult {1732		let collection_id = collection.id;17331734		let current_index = <ItemListIndex>::get(collection_id)1735			.checked_add(1)1736			.ok_or(Error::<T>::NumOverflow)?;17371738		let item_owner = item.owner.clone();1739		Self::add_token_index(collection_id, current_index, &item.owner)?;17401741		<ItemListIndex>::insert(collection_id, current_index);1742		<NftItemList<T>>::insert(collection_id, current_index, item);17431744		// Update balance1745		let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1746			.checked_add(1)1747			.ok_or(Error::<T>::NumOverflow)?;1748		<Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17491750		collection.log(ERC721Events::Transfer {1751			from: H160::default(),1752			to: *item_owner.as_eth(),1753			token_id: current_index.into(),1754		})?;1755		Self::deposit_event(RawEvent::ItemCreated(1756			collection_id,1757			current_index,1758			item_owner,1759		));1760		Ok(())1761	}17621763	fn burn_refungible_item(1764		collection: &CollectionHandle<T>,1765		item_id: TokenId,1766		owner: &T::CrossAccountId,1767	) -> DispatchResult {1768		let collection_id = collection.id;17691770		let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1771			.ok_or(Error::<T>::TokenNotFound)?;1772		let rft_balance = token1773			.owner1774			.iter()1775			.find(|&i| i.owner == *owner)1776			.ok_or(Error::<T>::TokenNotFound)?;1777		Self::remove_token_index(collection_id, item_id, owner)?;17781779		// update balance1780		let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1781			.checked_sub(rft_balance.fraction)1782			.ok_or(Error::<T>::NumOverflow)?;1783		<Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17841785		// Re-create owners list with sender removed1786		let index = token1787			.owner1788			.iter()1789			.position(|i| i.owner == *owner)1790			.expect("owned item is exists");1791		token.owner.remove(index);1792		let owner_count = token.owner.len();17931794		// Burn the token completely if this was the last (only) owner1795		if owner_count == 0 {1796			<ReFungibleItemList<T>>::remove(collection_id, item_id);1797			<VariableMetaDataBasket<T>>::remove(collection_id, item_id);1798		} else {1799			<ReFungibleItemList<T>>::insert(collection_id, item_id, token);1800		}18011802		Ok(())1803	}18041805	fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1806		let collection_id = collection.id;18071808		let item =1809			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1810		Self::remove_token_index(collection_id, item_id, &item.owner)?;18111812		// update balance1813		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1814			.checked_sub(1)1815			.ok_or(Error::<T>::NumOverflow)?;1816		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1817		<NftItemList<T>>::remove(collection_id, item_id);1818		<VariableMetaDataBasket<T>>::remove(collection_id, item_id);18191820		Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1821		Ok(())1822	}18231824	fn burn_fungible_item(1825		owner: &T::CrossAccountId,1826		collection: &CollectionHandle<T>,1827		value: u128,1828	) -> DispatchResult {1829		let collection_id = collection.id;18301831		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1832		ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18331834		// update balance1835		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1836			.checked_sub(value)1837			.ok_or(Error::<T>::NumOverflow)?;1838		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18391840		if balance.value - value > 0 {1841			balance.value -= value;1842			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1843		} else {1844			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1845		}18461847		collection.log(ERC20Events::Transfer {1848			from: *owner.as_eth(),1849			to: H160::default(),1850			value: value.into(),1851		})?;1852		Ok(())1853	}18541855	pub fn get_collection(1856		collection_id: CollectionId,1857	) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1858		Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1859	}18601861	fn check_owner_permissions(1862		target_collection: &CollectionHandle<T>,1863		subject: &T::AccountId,1864	) -> DispatchResult {1865		ensure!(1866			*subject == target_collection.owner,1867			Error::<T>::NoPermission1868		);18691870		Ok(())1871	}18721873	fn is_owner_or_admin_permissions(1874		collection: &CollectionHandle<T>,1875		subject: &T::CrossAccountId,1876	) -> bool {1877		*subject.as_sub() == collection.owner1878			|| <AdminList<T>>::get(collection.id).contains(subject)1879	}18801881	fn check_owner_or_admin_permissions(1882		collection: &CollectionHandle<T>,1883		subject: &T::CrossAccountId,1884	) -> DispatchResult {1885		ensure!(1886			Self::is_owner_or_admin_permissions(collection, subject),1887			Error::<T>::NoPermission1888		);18891890		Ok(())1891	}18921893	fn owned_amount(1894		subject: &T::CrossAccountId,1895		target_collection: &CollectionHandle<T>,1896		item_id: TokenId,1897	) -> Option<u128> {1898		let collection_id = target_collection.id;18991900		match target_collection.mode {1901			CollectionMode::NFT => {1902				(<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1903			}1904			CollectionMode::Fungible(_) => {1905				Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1906			}1907			CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1908				.owner1909				.iter()1910				.find(|i| i.owner == *subject)1911				.map(|i| i.fraction),1912			CollectionMode::Invalid => None,1913		}1914	}19151916	fn is_item_owner(1917		subject: &T::CrossAccountId,1918		target_collection: &CollectionHandle<T>,1919		item_id: TokenId,1920	) -> bool {1921		match target_collection.mode {1922			CollectionMode::Fungible(_) => true,1923			_ => Self::owned_amount(subject, target_collection, item_id).is_some(),1924		}1925	}19261927	fn check_white_list(1928		collection: &CollectionHandle<T>,1929		address: &T::CrossAccountId,1930	) -> DispatchResult {1931		let collection_id = collection.id;19321933		let mes = Error::<T>::AddresNotInWhiteList;1934		ensure!(1935			<WhiteList<T>>::contains_key(collection_id, address.as_sub()),1936			mes1937		);19381939		Ok(())1940	}19411942	/// Check if token exists. In case of Fungible, check if there is an entry for1943	/// the owner in fungible balances double map1944	fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1945		let collection_id = target_collection.id;1946		let exists = match target_collection.mode {1947			CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1948			CollectionMode::Fungible(_) => true,1949			CollectionMode::ReFungible => {1950				<ReFungibleItemList<T>>::contains_key(collection_id, item_id)1951			}1952			_ => false,1953		};19541955		ensure!(exists, Error::<T>::TokenNotFound);1956		Ok(())1957	}19581959	fn transfer_fungible(1960		collection: &CollectionHandle<T>,1961		value: u128,1962		owner: &T::CrossAccountId,1963		recipient: &T::CrossAccountId,1964	) -> DispatchResult {1965		let collection_id = collection.id;19661967		let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1968		ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19691970		// Send balance to recipient (updates balanceOf of recipient)1971		Self::add_fungible_item(collection, recipient, value)?;19721973		// update balanceOf of sender1974		<Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19751976		// Reduce or remove sender1977		if balance.value == value {1978			<FungibleItemList<T>>::remove(collection_id, owner.as_sub());1979		} else {1980			balance.value -= value;1981			<FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1982		}19831984		collection.log(ERC20Events::Transfer {1985			from: *owner.as_eth(),1986			to: *recipient.as_eth(),1987			value: value.into(),1988		})?;1989		Self::deposit_event(RawEvent::Transfer(1990			collection.id,1991			1,1992			owner.clone(),1993			recipient.clone(),1994			value,1995		));19961997		Ok(())1998	}19992000	fn transfer_refungible(2001		collection: &CollectionHandle<T>,2002		item_id: TokenId,2003		value: u128,2004		owner: T::CrossAccountId,2005		new_owner: T::CrossAccountId,2006	) -> DispatchResult {2007		let collection_id = collection.id;2008		let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2009			.ok_or(Error::<T>::TokenNotFound)?;20102011		let item = full_item2012			.owner2013			.iter()2014			.find(|i| i.owner == owner)2015			.ok_or(Error::<T>::TokenNotFound)?;2016		let amount = item.fraction;20172018		ensure!(amount >= value, Error::<T>::TokenValueTooLow);20192020		// update balance2021		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2022			.checked_sub(value)2023			.ok_or(Error::<T>::NumOverflow)?;2024		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20252026		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2027			.checked_add(value)2028			.ok_or(Error::<T>::NumOverflow)?;2029		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20302031		let old_owner = item.owner.clone();2032		let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20332034		let mut new_full_item = full_item.clone();2035		// transfer2036		if amount == value && !new_owner_has_account {2037			// change owner2038			// new owner do not have account2039			new_full_item2040				.owner2041				.iter_mut()2042				.find(|i| i.owner == owner)2043				.expect("old owner does present in refungible")2044				.owner = new_owner.clone();2045			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20462047			// update index collection2048			Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2049		} else {2050			new_full_item2051				.owner2052				.iter_mut()2053				.find(|i| i.owner == owner)2054				.expect("old owner does present in refungible")2055				.fraction -= value;20562057			// separate amount2058			if new_owner_has_account {2059				// new owner has account2060				new_full_item2061					.owner2062					.iter_mut()2063					.find(|i| i.owner == new_owner)2064					.expect("new owner has account")2065					.fraction += value;2066			} else {2067				// new owner do not have account2068				new_full_item.owner.push(Ownership {2069					owner: new_owner.clone(),2070					fraction: value,2071				});2072				Self::add_token_index(collection_id, item_id, &new_owner)?;2073			}20742075			<ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2076		}20772078		Self::deposit_event(RawEvent::Transfer(2079			collection.id,2080			item_id,2081			owner,2082			new_owner,2083			amount,2084		));20852086		Ok(())2087	}20882089	fn transfer_nft(2090		collection: &CollectionHandle<T>,2091		item_id: TokenId,2092		sender: T::CrossAccountId,2093		new_owner: T::CrossAccountId,2094	) -> DispatchResult {2095		let collection_id = collection.id;2096		let mut item =2097			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20982099		ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21002101		// update balance2102		let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2103			.checked_sub(1)2104			.ok_or(Error::<T>::NumOverflow)?;2105		<Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21062107		let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2108			.checked_add(1)2109			.ok_or(Error::<T>::NumOverflow)?;2110		<Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21112112		// change owner2113		let old_owner = item.owner.clone();2114		item.owner = new_owner.clone();2115		<NftItemList<T>>::insert(collection_id, item_id, item);21162117		// update index collection2118		Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21192120		collection.log(ERC721Events::Transfer {2121			from: *sender.as_eth(),2122			to: *new_owner.as_eth(),2123			token_id: item_id.into(),2124		})?;2125		Self::deposit_event(RawEvent::Transfer(2126			collection.id,2127			item_id,2128			sender,2129			new_owner,2130			1,2131		));21322133		Ok(())2134	}21352136	fn set_re_fungible_variable_data(2137		collection: &CollectionHandle<T>,2138		item_id: TokenId,2139		data: Vec<u8>,2140	) -> DispatchResult {2141		let collection_id = collection.id;2142		let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2143			.ok_or(Error::<T>::TokenNotFound)?;21442145		item.variable_data = data;21462147		<ReFungibleItemList<T>>::insert(collection_id, item_id, item);21482149		Ok(())2150	}21512152	fn set_nft_variable_data(2153		collection: &CollectionHandle<T>,2154		item_id: TokenId,2155		data: Vec<u8>,2156	) -> DispatchResult {2157		let collection_id = collection.id;2158		let mut item =2159			<NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21602161		item.variable_data = data;21622163		<NftItemList<T>>::insert(collection_id, item_id, item);21642165		Ok(())2166	}21672168	#[allow(dead_code)]2169	fn init_collection(item: &Collection<T>) {2170		// check params2171		assert!(2172			item.decimal_points <= MAX_DECIMAL_POINTS,2173			"decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2174		);2175		assert!(2176			item.name.len() <= 64,2177			"Collection name can not be longer than 63 char"2178		);2179		assert!(2180			item.name.len() <= 256,2181			"Collection description can not be longer than 255 char"2182		);2183		assert!(2184			item.token_prefix.len() <= 16,2185			"Token prefix can not be longer than 15 char"2186		);21872188		// Generate next collection ID2189		let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21902191		CreatedCollectionCount::put(next_id);2192	}21932194	#[allow(dead_code)]2195	fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2196		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21972198		Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21992200		<ItemListIndex>::insert(collection_id, current_index);22012202		// Update balance2203		let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2204			.checked_add(1)2205			.unwrap();2206		<Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2207	}22082209	#[allow(dead_code)]2210	fn init_fungible_token(2211		collection_id: CollectionId,2212		owner: &T::CrossAccountId,2213		item: &FungibleItemType,2214	) {2215		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22162217		Self::add_token_index(collection_id, current_index, owner).unwrap();22182219		<ItemListIndex>::insert(collection_id, current_index);22202221		// Update balance2222		let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2223			.checked_add(item.value)2224			.unwrap();2225		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2226	}22272228	#[allow(dead_code)]2229	fn init_refungible_token(2230		collection_id: CollectionId,2231		item: &ReFungibleItemType<T::CrossAccountId>,2232	) {2233		let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22342235		let value = item.owner.first().unwrap().fraction;2236		let owner = item.owner.first().unwrap().owner.clone();22372238		Self::add_token_index(collection_id, current_index, &owner).unwrap();22392240		<ItemListIndex>::insert(collection_id, current_index);22412242		// Update balance2243		let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2244			.checked_add(value)2245			.unwrap();2246		<Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2247	}22482249	fn add_token_index(2250		collection_id: CollectionId,2251		item_index: TokenId,2252		owner: &T::CrossAccountId,2253	) -> DispatchResult {2254		// add to account limit2255		if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2256			// bound Owned tokens by a single address2257			let count = <AccountItemCount<T>>::get(owner.as_sub());2258			ensure!(2259				count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2260				Error::<T>::AddressOwnershipLimitExceeded2261			);22622263			<AccountItemCount<T>>::insert(2264				owner.as_sub(),2265				count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2266			);2267		} else {2268			<AccountItemCount<T>>::insert(owner.as_sub(), 1);2269		}22702271		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2272		if list_exists {2273			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2274			let item_contains = list.contains(&item_index.clone());22752276			if !item_contains {2277				list.push(item_index);2278			}22792280			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2281		} else {2282			let itm = vec![item_index];2283			<AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2284		}22852286		Ok(())2287	}22882289	fn remove_token_index(2290		collection_id: CollectionId,2291		item_index: TokenId,2292		owner: &T::CrossAccountId,2293	) -> DispatchResult {2294		// update counter2295		<AccountItemCount<T>>::insert(2296			owner.as_sub(),2297			<AccountItemCount<T>>::get(owner.as_sub())2298				.checked_sub(1)2299				.ok_or(Error::<T>::NumOverflow)?,2300		);23012302		let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2303		if list_exists {2304			let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2305			let item_contains = list.contains(&item_index.clone());23062307			if item_contains {2308				list.retain(|&item| item != item_index);2309				<AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2310			}2311		}23122313		Ok(())2314	}23152316	fn move_token_index(2317		collection_id: CollectionId,2318		item_index: TokenId,2319		old_owner: &T::CrossAccountId,2320		new_owner: &T::CrossAccountId,2321	) -> DispatchResult {2322		Self::remove_token_index(collection_id, item_index, old_owner)?;2323		Self::add_token_index(collection_id, item_index, new_owner)?;23242325		Ok(())2326	}2327}23282329sp_api::decl_runtime_apis! {2330	pub trait NftApi {2331		/// Used for ethereum integration2332		fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2333	}2334}