difftreelog
test remove ChainLimits leftovers
in: master
8 files changed
pallets/nft/Cargo.tomldiffbeforeafterboth--- a/pallets/nft/Cargo.toml
+++ b/pallets/nft/Cargo.toml
@@ -41,6 +41,7 @@
'evm-coder/std',
'pallet-evm-coder-substrate/std',
]
+limit-testing = ["nft-data-structs/limit-testing"]
################################################################################
# Substrate Dependencies
pallets/nft/src/default_weights.rsdiffbeforeafterboth--- a/pallets/nft/src/default_weights.rs
+++ b/pallets/nft/src/default_weights.rs
@@ -117,11 +117,6 @@
.saturating_add(DbWeight::get().reads(2_u64))
.saturating_add(DbWeight::get().writes(1_u64))
}
- fn set_chain_limits() -> Weight {
- 1_300_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
fn set_contract_sponsoring_rate_limit() -> Weight {
3_500_000_u64
.saturating_add(DbWeight::get().reads(0_u64))
pallets/nft/src/lib.rsdiffbeforeafterboth1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]7#![cfg_attr(not(feature = "std"), no_std)]8#![allow(9 clippy::too_many_arguments,10 clippy::unnecessary_mut_passed,11 clippy::unused_unit12)]1314extern crate alloc;1516pub use serde::{Serialize, Deserialize};1718pub use frame_support::{19 construct_runtime, decl_event, decl_module, decl_storage, decl_error,20 dispatch::DispatchResult,21 ensure, fail, parameter_types,22 traits::{23 Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,24 Randomness, IsSubType, WithdrawReasons,25 },26 weights::{27 constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},28 DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,29 WeightToFeePolynomial, DispatchClass,30 },31 StorageValue, transactional,32};3334use frame_system::{self as system, ensure_signed};35use sp_core::H160;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};39use nft_data_structs::{40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,41 CUSTOM_DATA_LIMIT, COLLECTION_NUMBER_LIMIT, ACCOUNT_TOKEN_OWNERSHIP_LIMIT,42 VARIABLE_ON_CHAIN_SCHEMA_LIMIT, CONST_ON_CHAIN_SCHEMA_LIMIT, COLLECTION_ADMINS_LIMIT,43 OFFCHAIN_SCHEMA_LIMIT, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45 FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;6667pub trait WeightInfo {68 fn create_collection() -> Weight;69 fn destroy_collection() -> Weight;70 fn add_to_white_list() -> Weight;71 fn remove_from_white_list() -> Weight;72 fn set_public_access_mode() -> Weight;73 fn set_mint_permission() -> Weight;74 fn change_collection_owner() -> Weight;75 fn add_collection_admin() -> Weight;76 fn remove_collection_admin() -> Weight;77 fn set_collection_sponsor() -> Weight;78 fn confirm_sponsorship() -> Weight;79 fn remove_collection_sponsor() -> Weight;80 fn create_item(s: usize) -> Weight;81 fn burn_item() -> Weight;82 fn transfer() -> Weight;83 fn approve() -> Weight;84 fn transfer_from() -> Weight;85 fn set_offchain_schema() -> Weight;86 fn set_const_on_chain_schema() -> Weight;87 fn set_variable_on_chain_schema() -> Weight;88 fn set_variable_meta_data() -> Weight;89 fn enable_contract_sponsoring() -> Weight;90 fn set_schema_version() -> Weight;91 fn set_chain_limits() -> Weight;92 fn set_contract_sponsoring_rate_limit() -> Weight;93 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;94 fn toggle_contract_white_list() -> Weight;95 fn add_to_contract_white_list() -> Weight;96 fn remove_from_contract_white_list() -> Weight;97 fn set_collection_limits() -> Weight;98}99100decl_error! {101 /// Error for non-fungible-token module.102 pub enum Error for Module<T: Config> {103 /// Total collections bound exceeded.104 TotalCollectionsLimitExceeded,105 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.106 CollectionDecimalPointLimitExceeded,107 /// Collection name can not be longer than 63 char.108 CollectionNameLimitExceeded,109 /// Collection description can not be longer than 255 char.110 CollectionDescriptionLimitExceeded,111 /// Token prefix can not be longer than 15 char.112 CollectionTokenPrefixLimitExceeded,113 /// This collection does not exist.114 CollectionNotFound,115 /// Item not exists.116 TokenNotFound,117 /// Admin not found118 AdminNotFound,119 /// Arithmetic calculation overflow.120 NumOverflow,121 /// Account already has admin role.122 AlreadyAdmin,123 /// You do not own this collection.124 NoPermission,125 /// This address is not set as sponsor, use setCollectionSponsor first.126 ConfirmUnsetSponsorFail,127 /// Collection is not in mint mode.128 PublicMintingNotAllowed,129 /// Sender parameter and item owner must be equal.130 MustBeTokenOwner,131 /// Item balance not enough.132 TokenValueTooLow,133 /// Size of item is too large.134 NftSizeLimitExceeded,135 /// No approve found136 ApproveNotFound,137 /// Requested value more than approved.138 TokenValueNotEnough,139 /// Only approved addresses can call this method.140 ApproveRequired,141 /// Address is not in white list.142 AddresNotInWhiteList,143 /// Number of collection admins bound exceeded.144 CollectionAdminsLimitExceeded,145 /// Owned tokens by a single address bound exceeded.146 AddressOwnershipLimitExceeded,147 /// Length of items properties must be greater than 0.148 EmptyArgument,149 /// const_data exceeded data limit.150 TokenConstDataLimitExceeded,151 /// variable_data exceeded data limit.152 TokenVariableDataLimitExceeded,153 /// Not NFT item data used to mint in NFT collection.154 NotNftDataUsedToMintNftCollectionToken,155 /// Not Fungible item data used to mint in Fungible collection.156 NotFungibleDataUsedToMintFungibleCollectionToken,157 /// Not Re Fungible item data used to mint in Re Fungible collection.158 NotReFungibleDataUsedToMintReFungibleCollectionToken,159 /// Unexpected collection type.160 UnexpectedCollectionType,161 /// Can't store metadata in fungible tokens.162 CantStoreMetadataInFungibleTokens,163 /// Collection token limit exceeded164 CollectionTokenLimitExceeded,165 /// Account token limit exceeded per collection166 AccountTokenLimitExceeded,167 /// Collection limit bounds per collection exceeded168 CollectionLimitBoundsExceeded,169 /// Tried to enable permissions which are only permitted to be disabled170 OwnerPermissionsCantBeReverted,171 /// Schema data size limit bound exceeded172 SchemaDataLimitExceeded,173 /// Maximum refungibility exceeded174 WrongRefungiblePieces,175 /// createRefungible should be called with one owner176 BadCreateRefungibleCall,177 /// Gas limit exceeded178 OutOfGas,179 /// Collection settings not allowing items transferring180 TransferNotAllowed,181 }182}183184#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]185pub struct CollectionHandle<T: Config> {186 pub id: CollectionId,187 collection: Collection<T>,188 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,189}190impl<T: Config> CollectionHandle<T> {191 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {192 <CollectionById<T>>::get(id).map(|collection| Self {193 id,194 collection,195 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(196 eth::collection_id_to_address(id),197 gas_limit,198 ),199 })200 }201 pub fn get(id: CollectionId) -> Option<Self> {202 Self::get_with_gas_limit(id, u64::MAX)203 }204 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {205 self.recorder.log_sub(log)206 }207 fn consume_gas(&self, gas: u64) -> DispatchResult {208 self.recorder.consume_gas_sub(gas)209 }210 pub fn submit_logs(self) -> DispatchResult {211 self.recorder.submit_logs()212 }213 pub fn save(self) -> DispatchResult {214 self.recorder.submit_logs()?;215 <CollectionById<T>>::insert(self.id, self.collection);216 Ok(())217 }218}219impl<T: Config> Deref for CollectionHandle<T> {220 type Target = Collection<T>;221222 fn deref(&self) -> &Self::Target {223 &self.collection224 }225}226227impl<T: Config> DerefMut for CollectionHandle<T> {228 fn deref_mut(&mut self) -> &mut Self::Target {229 &mut self.collection230 }231}232233pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {234 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;235236 /// Weight information for extrinsics in this pallet.237 type WeightInfo: WeightInfo;238239 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;240 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;241242 type CrossAccountId: CrossAccountId<Self::AccountId>;243 type Currency: Currency<Self::AccountId>;244 type CollectionCreationPrice: Get<245 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,246 >;247 type TreasuryAccountId: Get<Self::AccountId>;248}249250// # Used definitions251//252// ## User control levels253//254// chain-controlled - key is uncontrolled by user255// i.e autoincrementing index256// can use non-cryptographic hash257// real - key is controlled by user258// but it is hard to generate enough colliding values, i.e owner of signed txs259// can use non-cryptographic hash260// controlled - key is completly controlled by users261// i.e maps with mutable keys262// should use cryptographic hash263//264// ## User control level downgrade reasons265//266// ?1 - chain-controlled -> controlled267// collections/tokens can be destroyed, resulting in massive holes268// ?2 - chain-controlled -> controlled269// same as ?1, but can be only added, resulting in easier exploitation270// ?3 - real -> controlled271// no confirmation required, so addresses can be easily generated272decl_storage! {273 trait Store for Module<T: Config> as Nft {274275 //#region Private members276 /// Id of next collection277 CreatedCollectionCount: u32;278 /// Used for migrations279 ChainVersion: u64;280 /// Id of last collection token281 /// Collection id (controlled?1)282 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;283 //#endregion284285 //#region Bound counters286 /// Amount of collections destroyed, used for total amount tracking with287 /// CreatedCollectionCount288 DestroyedCollectionCount: u32;289 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)290 /// Account id (real)291 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;292 //#endregion293294 //#region Basic collections295 /// Collection info296 /// Collection id (controlled?1)297 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;298 /// List of collection admins299 /// Collection id (controlled?2)300 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;301 /// Whitelisted collection users302 /// Collection id (controlled?2), user id (controlled?3)303 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;304 //#endregion305306 /// How many of collection items user have307 /// Collection id (controlled?2), account id (real)308 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;309310 /// Amount of items which spender can transfer out of owners account (via transferFrom)311 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))312 /// TODO: Off chain worker should remove from this map when token gets removed313 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;314315 //#region Item collections316 /// Collection id (controlled?2), token id (controlled?1)317 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;318 /// Collection id (controlled?2), owner (controlled?2)319 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;320 /// Collection id (controlled?2), token id (controlled?1)321 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;322 //#endregion323324 //#region Index list325 /// Collection id (controlled?2), tokens owner (controlled?2)326 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;327 //#endregion328329 //#region Tokens transfer rate limit baskets330 /// (Collection id (controlled?2), who created (real))331 /// TODO: Off chain worker should remove from this map when collection gets removed332 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;333 /// Collection id (controlled?2), token id (controlled?2)334 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;335 /// Collection id (controlled?2), owning user (real)336 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;337 /// Collection id (controlled?2), token id (controlled?2)338 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;339 //#endregion340341 /// Variable metadata sponsoring342 /// Collection id (controlled?2), token id (controlled?2)343 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;344 }345 add_extra_genesis {346 build(|config: &GenesisConfig<T>| {347 // Modification of storage348 for (_num, _c) in &config.collection_id {349 <Module<T>>::init_collection(_c);350 }351352 for (_num, _c, _i) in &config.nft_item_id {353 <Module<T>>::init_nft_token(*_c, _i);354 }355356 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {357 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);358 }359360 for (_num, _c, _i) in &config.refungible_item_id {361 <Module<T>>::init_refungible_token(*_c, _i);362 }363 })364 }365}366367decl_event!(368 pub enum Event<T>369 where370 AccountId = <T as frame_system::Config>::AccountId,371 CrossAccountId = <T as Config>::CrossAccountId,372 {373 /// New collection was created374 ///375 /// # Arguments376 ///377 /// * collection_id: Globally unique identifier of newly created collection.378 ///379 /// * mode: [CollectionMode] converted into u8.380 ///381 /// * account_id: Collection owner.382 CollectionCreated(CollectionId, u8, AccountId),383384 /// New item was created.385 ///386 /// # Arguments387 ///388 /// * collection_id: Id of the collection where item was created.389 ///390 /// * item_id: Id of an item. Unique within the collection.391 ///392 /// * recipient: Owner of newly created item393 ItemCreated(CollectionId, TokenId, CrossAccountId),394395 /// Collection item was burned.396 ///397 /// # Arguments398 ///399 /// collection_id.400 ///401 /// item_id: Identifier of burned NFT.402 ItemDestroyed(CollectionId, TokenId),403404 /// Item was transferred405 ///406 /// * collection_id: Id of collection to which item is belong407 ///408 /// * item_id: Id of an item409 ///410 /// * sender: Original owner of item411 ///412 /// * recipient: New owner of item413 ///414 /// * amount: Always 1 for NFT415 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),416417 /// * collection_id418 ///419 /// * item_id420 ///421 /// * sender422 ///423 /// * spender424 ///425 /// * amount426 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),427 }428);429430decl_module! {431 pub struct Module<T: Config> for enum Call432 where433 origin: T::Origin434 {435 fn deposit_event() = default;436 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;437 type Error = Error<T>;438439 fn on_initialize(_now: T::BlockNumber) -> Weight {440 0441 }442443 /// 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.444 ///445 /// # Permissions446 ///447 /// * Anyone.448 ///449 /// # Arguments450 ///451 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.452 ///453 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.454 ///455 /// * token_prefix: UTF-8 string with token prefix.456 ///457 /// * mode: [CollectionMode] collection type and type dependent data.458 // returns collection ID459 #[weight = <T as Config>::WeightInfo::create_collection()]460 #[transactional]461 pub fn create_collection(origin,462 collection_name: Vec<u16>,463 collection_description: Vec<u16>,464 token_prefix: Vec<u8>,465 mode: CollectionMode) -> DispatchResult {466467 // Anyone can create a collection468 let who = ensure_signed(origin)?;469470 // Take a (non-refundable) deposit of collection creation471 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();472 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(473 &T::TreasuryAccountId::get(),474 T::CollectionCreationPrice::get(),475 ));476 <T as Config>::Currency::settle(477 &who,478 imbalance,479 WithdrawReasons::TRANSFER,480 ExistenceRequirement::KeepAlive,481 ).map_err(|_| Error::<T>::NoPermission)?;482483 let decimal_points = match mode {484 CollectionMode::Fungible(points) => points,485 _ => 0486 };487488 let created_count = CreatedCollectionCount::get();489 let destroyed_count = DestroyedCollectionCount::get();490491 // bound Total number of collections492 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);493494 // check params495 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);496 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);497 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);498 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);499500 // Generate next collection ID501 let next_id = created_count502 .checked_add(1)503 .ok_or(Error::<T>::NumOverflow)?;504505 CreatedCollectionCount::put(next_id);506507 let limits = CollectionLimits {508 sponsored_data_size: CUSTOM_DATA_LIMIT,509 ..Default::default()510 };511512 // Create new collection513 let new_collection = Collection {514 owner: who.clone(),515 name: collection_name,516 mode: mode.clone(),517 mint_mode: false,518 access: AccessMode::Normal,519 description: collection_description,520 decimal_points,521 token_prefix,522 offchain_schema: Vec::new(),523 schema_version: SchemaVersion::ImageURL,524 sponsorship: SponsorshipState::Disabled,525 variable_on_chain_schema: Vec::new(),526 const_on_chain_schema: Vec::new(),527 limits,528 transfers_enabled: true,529 };530531 // Add new collection to map532 <CollectionById<T>>::insert(next_id, new_collection);533534 // call event535 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));536537 Ok(())538 }539540 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.541 ///542 /// # Permissions543 ///544 /// * Collection Owner.545 ///546 /// # Arguments547 ///548 /// * collection_id: collection to destroy.549 #[weight = <T as Config>::WeightInfo::destroy_collection()]550 #[transactional]551 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {552553 let sender = ensure_signed(origin)?;554 let collection = Self::get_collection(collection_id)?;555 Self::check_owner_permissions(&collection, &sender)?;556 if !collection.limits.owner_can_destroy {557 fail!(Error::<T>::NoPermission);558 }559560 <AddressTokens<T>>::remove_prefix(collection_id, None);561 <Allowances<T>>::remove_prefix(collection_id, None);562 <Balance<T>>::remove_prefix(collection_id, None);563 <ItemListIndex>::remove(collection_id);564 <AdminList<T>>::remove(collection_id);565 <CollectionById<T>>::remove(collection_id);566 <WhiteList<T>>::remove_prefix(collection_id, None);567568 <NftItemList<T>>::remove_prefix(collection_id, None);569 <FungibleItemList<T>>::remove_prefix(collection_id, None);570 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);571572 <NftTransferBasket<T>>::remove_prefix(collection_id, None);573 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);574 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);575576 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);577578 DestroyedCollectionCount::put(DestroyedCollectionCount::get()579 .checked_add(1)580 .ok_or(Error::<T>::NumOverflow)?);581582 Ok(())583 }584585 /// Add an address to white list.586 ///587 /// # Permissions588 ///589 /// * Collection Owner590 /// * Collection Admin591 ///592 /// # Arguments593 ///594 /// * collection_id.595 ///596 /// * address.597 #[weight = <T as Config>::WeightInfo::add_to_white_list()]598 #[transactional]599 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{600601 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);602 let collection = Self::get_collection(collection_id)?;603604 Self::toggle_white_list_internal(605 &sender,606 &collection,607 &address,608 true,609 )?;610611 Ok(())612 }613614 /// Remove an address from white list.615 ///616 /// # Permissions617 ///618 /// * Collection Owner619 /// * Collection Admin620 ///621 /// # Arguments622 ///623 /// * collection_id.624 ///625 /// * address.626 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]627 #[transactional]628 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{629630 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);631 let collection = Self::get_collection(collection_id)?;632633 Self::toggle_white_list_internal(634 &sender,635 &collection,636 &address,637 false,638 )?;639640 Ok(())641 }642643 /// Toggle between normal and white list access for the methods with access for `Anyone`.644 ///645 /// # Permissions646 ///647 /// * Collection Owner.648 ///649 /// # Arguments650 ///651 /// * collection_id.652 ///653 /// * mode: [AccessMode]654 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]655 #[transactional]656 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult657 {658 let sender = ensure_signed(origin)?;659660 let mut target_collection = Self::get_collection(collection_id)?;661 Self::check_owner_permissions(&target_collection, &sender)?;662 target_collection.access = mode;663 target_collection.save()664 }665666 /// Allows Anyone to create tokens if:667 /// * White List is enabled, and668 /// * Address is added to white list, and669 /// * This method was called with True parameter670 ///671 /// # Permissions672 /// * Collection Owner673 ///674 /// # Arguments675 ///676 /// * collection_id.677 ///678 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.679 #[weight = <T as Config>::WeightInfo::set_mint_permission()]680 #[transactional]681 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult682 {683 let sender = ensure_signed(origin)?;684685 let mut target_collection = Self::get_collection(collection_id)?;686 Self::check_owner_permissions(&target_collection, &sender)?;687 target_collection.mint_mode = mint_permission;688 target_collection.save()689 }690691 /// Change the owner of the collection.692 ///693 /// # Permissions694 ///695 /// * Collection Owner.696 ///697 /// # Arguments698 ///699 /// * collection_id.700 ///701 /// * new_owner.702 #[weight = <T as Config>::WeightInfo::change_collection_owner()]703 #[transactional]704 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {705706 let sender = ensure_signed(origin)?;707 let mut target_collection = Self::get_collection(collection_id)?;708 Self::check_owner_permissions(&target_collection, &sender)?;709 target_collection.owner = new_owner;710 target_collection.save()711 }712713 /// Adds an admin of the Collection.714 /// 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.715 ///716 /// # Permissions717 ///718 /// * Collection Owner.719 /// * Collection Admin.720 ///721 /// # Arguments722 ///723 /// * collection_id: ID of the Collection to add admin for.724 ///725 /// * new_admin_id: Address of new admin to add.726 #[weight = <T as Config>::WeightInfo::add_collection_admin()]727 #[transactional]728 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {729 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);730 let collection = Self::get_collection(collection_id)?;731 Self::check_owner_or_admin_permissions(&collection, &sender)?;732 let mut admin_arr = <AdminList<T>>::get(collection_id);733734 match admin_arr.binary_search(&new_admin_id) {735 Ok(_) => {},736 Err(idx) => {737 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);738 admin_arr.insert(idx, new_admin_id);739 <AdminList<T>>::insert(collection_id, admin_arr);740 }741 }742 Ok(())743 }744745 /// 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.746 ///747 /// # Permissions748 ///749 /// * Collection Owner.750 /// * Collection Admin.751 ///752 /// # Arguments753 ///754 /// * collection_id: ID of the Collection to remove admin for.755 ///756 /// * account_id: Address of admin to remove.757 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]758 #[transactional]759 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {760 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);761 let collection = Self::get_collection(collection_id)?;762 Self::check_owner_or_admin_permissions(&collection, &sender)?;763 let mut admin_arr = <AdminList<T>>::get(collection_id);764765 if let Ok(idx) = admin_arr.binary_search(&account_id) {766 admin_arr.remove(idx);767 <AdminList<T>>::insert(collection_id, admin_arr);768 }769 Ok(())770 }771772 /// # Permissions773 ///774 /// * Collection Owner775 ///776 /// # Arguments777 ///778 /// * collection_id.779 ///780 /// * new_sponsor.781 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]782 #[transactional]783 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {784 let sender = ensure_signed(origin)?;785 let mut target_collection = Self::get_collection(collection_id)?;786 Self::check_owner_permissions(&target_collection, &sender)?;787788 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);789 target_collection.save()790 }791792 /// # Permissions793 ///794 /// * Sponsor.795 ///796 /// # Arguments797 ///798 /// * collection_id.799 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]800 #[transactional]801 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {802 let sender = ensure_signed(origin)?;803804 let mut target_collection = Self::get_collection(collection_id)?;805 ensure!(806 target_collection.sponsorship.pending_sponsor() == Some(&sender),807 Error::<T>::ConfirmUnsetSponsorFail808 );809810 target_collection.sponsorship = SponsorshipState::Confirmed(sender);811 target_collection.save()812 }813814 /// Switch back to pay-per-own-transaction model.815 ///816 /// # Permissions817 ///818 /// * Collection owner.819 ///820 /// # Arguments821 ///822 /// * collection_id.823 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]824 #[transactional]825 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {826 let sender = ensure_signed(origin)?;827828 let mut target_collection = Self::get_collection(collection_id)?;829 Self::check_owner_permissions(&target_collection, &sender)?;830831 target_collection.sponsorship = SponsorshipState::Disabled;832 target_collection.save()833 }834835 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.836 ///837 /// # Permissions838 ///839 /// * Collection Owner.840 /// * Collection Admin.841 /// * Anyone if842 /// * White List is enabled, and843 /// * Address is added to white list, and844 /// * MintPermission is enabled (see SetMintPermission method)845 ///846 /// # Arguments847 ///848 /// * collection_id: ID of the collection.849 ///850 /// * owner: Address, initial owner of the NFT.851 ///852 /// * data: Token data to store on chain.853 // #[weight =854 // (130_000_000 as Weight)855 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))856 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))857 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]858859 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]860 #[transactional]861 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {862 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);863 let collection = Self::get_collection(collection_id)?;864865 Self::create_item_internal(&sender, &collection, &owner, data)?;866867 collection.submit_logs()868 }869870 /// This method creates multiple items in a collection created with CreateCollection method.871 ///872 /// # Permissions873 ///874 /// * Collection Owner.875 /// * Collection Admin.876 /// * Anyone if877 /// * White List is enabled, and878 /// * Address is added to white list, and879 /// * MintPermission is enabled (see SetMintPermission method)880 ///881 /// # Arguments882 ///883 /// * collection_id: ID of the collection.884 ///885 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].886 ///887 /// * owner: Address, initial owner of the NFT.888 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()889 .map(|data| { data.data_size() })890 .sum())]891 #[transactional]892 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {893894 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);895 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896 let collection = Self::get_collection(collection_id)?;897898 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;899900 collection.submit_logs()901 }902903 // TODO! transaction weight904905 /// Set transfers_enabled value for particular collection906 ///907 /// # Permissions908 ///909 /// * Collection Owner.910 ///911 /// # Arguments912 ///913 /// * collection_id: ID of the collection.914 ///915 /// * value: New flag value.916 #[weight = <T as Config>::WeightInfo::burn_item()]917 #[transactional]918 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {919920 let sender = ensure_signed(origin)?;921 let mut target_collection = Self::get_collection(collection_id)?;922923 Self::check_owner_permissions(&target_collection, &sender)?;924925 target_collection.transfers_enabled = value;926 target_collection.save()927 }928929 /// Destroys a concrete instance of NFT.930 ///931 /// # Permissions932 ///933 /// * Collection Owner.934 /// * Collection Admin.935 /// * Current NFT Owner.936 ///937 /// # Arguments938 ///939 /// * collection_id: ID of the collection.940 ///941 /// * item_id: ID of NFT to burn.942 #[weight = <T as Config>::WeightInfo::burn_item()]943 #[transactional]944 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {945946 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);947 let target_collection = Self::get_collection(collection_id)?;948949 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;950951 target_collection.submit_logs()952 }953954 /// Change ownership of the token.955 ///956 /// # Permissions957 ///958 /// * Collection Owner959 /// * Collection Admin960 /// * Current NFT owner961 ///962 /// # Arguments963 ///964 /// * recipient: Address of token recipient.965 ///966 /// * collection_id.967 ///968 /// * item_id: ID of the item969 /// * Non-Fungible Mode: Required.970 /// * Fungible Mode: Ignored.971 /// * Re-Fungible Mode: Required.972 ///973 /// * value: Amount to transfer.974 /// * Non-Fungible Mode: Ignored975 /// * Fungible Mode: Must specify transferred amount976 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)977 #[weight = <T as Config>::WeightInfo::transfer()]978 #[transactional]979 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {980 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981 let collection = Self::get_collection(collection_id)?;982983 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;984985 collection.submit_logs()986 }987988 /// Set, change, or remove approved address to transfer the ownership of the NFT.989 ///990 /// # Permissions991 ///992 /// * Collection Owner993 /// * Collection Admin994 /// * Current NFT owner995 ///996 /// # Arguments997 ///998 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).999 ///1000 /// * collection_id.1001 ///1002 /// * item_id: ID of the item.1003 #[weight = <T as Config>::WeightInfo::approve()]1004 #[transactional]1005 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1006 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1007 let collection = Self::get_collection(collection_id)?;10081009 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10101011 collection.submit_logs()1012 }10131014 /// 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.1015 ///1016 /// # Permissions1017 /// * Collection Owner1018 /// * Collection Admin1019 /// * Current NFT owner1020 /// * Address approved by current NFT owner1021 ///1022 /// # Arguments1023 ///1024 /// * from: Address that owns token.1025 ///1026 /// * recipient: Address of token recipient.1027 ///1028 /// * collection_id.1029 ///1030 /// * item_id: ID of the item.1031 ///1032 /// * value: Amount to transfer.1033 #[weight = <T as Config>::WeightInfo::transfer_from()]1034 #[transactional]1035 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1036 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1037 let collection = Self::get_collection(collection_id)?;10381039 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10401041 collection.submit_logs()1042 }1043 // #[weight = 0]1044 // // let no_perm_mes = "You do not have permissions to modify this collection";1045 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1046 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1047 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10481049 // // // on_nft_received call10501051 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10521053 // Ok(())1054 // }10551056 /// Set off-chain data schema.1057 ///1058 /// # Permissions1059 ///1060 /// * Collection Owner1061 /// * Collection Admin1062 ///1063 /// # Arguments1064 ///1065 /// * collection_id.1066 ///1067 /// * schema: String representing the offchain data schema.1068 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1069 #[transactional]1070 pub fn set_variable_meta_data (1071 origin,1072 collection_id: CollectionId,1073 item_id: TokenId,1074 data: Vec<u8>1075 ) -> DispatchResult {1076 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10771078 let collection = Self::get_collection(collection_id)?;10791080 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10811082 Ok(())1083 }10841085 /// Set schema standard1086 /// ImageURL1087 /// Unique1088 ///1089 /// # Permissions1090 ///1091 /// * Collection Owner1092 /// * Collection Admin1093 ///1094 /// # Arguments1095 ///1096 /// * collection_id.1097 ///1098 /// * schema: SchemaVersion: enum1099 #[weight = <T as Config>::WeightInfo::set_schema_version()]1100 #[transactional]1101 pub fn set_schema_version(1102 origin,1103 collection_id: CollectionId,1104 version: SchemaVersion1105 ) -> DispatchResult {1106 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1107 let mut target_collection = Self::get_collection(collection_id)?;1108 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1109 target_collection.schema_version = version;1110 target_collection.save()1111 }11121113 /// Set off-chain data schema.1114 ///1115 /// # Permissions1116 ///1117 /// * Collection Owner1118 /// * Collection Admin1119 ///1120 /// # Arguments1121 ///1122 /// * collection_id.1123 ///1124 /// * schema: String representing the offchain data schema.1125 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1126 #[transactional]1127 pub fn set_offchain_schema(1128 origin,1129 collection_id: CollectionId,1130 schema: Vec<u8>1131 ) -> DispatchResult {1132 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1133 let mut target_collection = Self::get_collection(collection_id)?;1134 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11351136 // check schema limit1137 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11381139 target_collection.offchain_schema = schema;1140 target_collection.save()1141 }11421143 /// Set const on-chain data schema.1144 ///1145 /// # Permissions1146 ///1147 /// * Collection Owner1148 /// * Collection Admin1149 ///1150 /// # Arguments1151 ///1152 /// * collection_id.1153 ///1154 /// * schema: String representing the const on-chain data schema.1155 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1156 #[transactional]1157 pub fn set_const_on_chain_schema (1158 origin,1159 collection_id: CollectionId,1160 schema: Vec<u8>1161 ) -> DispatchResult {1162 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1163 let mut target_collection = Self::get_collection(collection_id)?;1164 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11651166 // check schema limit1167 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11681169 target_collection.const_on_chain_schema = schema;1170 target_collection.save()1171 }11721173 /// Set variable on-chain data schema.1174 ///1175 /// # Permissions1176 ///1177 /// * Collection Owner1178 /// * Collection Admin1179 ///1180 /// # Arguments1181 ///1182 /// * collection_id.1183 ///1184 /// * schema: String representing the variable on-chain data schema.1185 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1186 #[transactional]1187 pub fn set_variable_on_chain_schema (1188 origin,1189 collection_id: CollectionId,1190 schema: Vec<u8>1191 ) -> DispatchResult {1192 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1193 let mut target_collection = Self::get_collection(collection_id)?;1194 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11951196 // check schema limit1197 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11981199 target_collection.variable_on_chain_schema = schema;1200 target_collection.save()1201 }12021203 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1204 #[transactional]1205 pub fn set_collection_limits(1206 origin,1207 collection_id: u32,1208 new_limits: CollectionLimits<T::BlockNumber>,1209 ) -> DispatchResult {1210 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1211 let mut target_collection = Self::get_collection(collection_id)?;1212 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1213 let old_limits = &target_collection.limits;12141215 // collection bounds1216 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1217 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1218 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1219 Error::<T>::CollectionLimitBoundsExceeded);12201221 // token_limit check prev1222 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1223 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12241225 ensure!(1226 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1227 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1228 Error::<T>::OwnerPermissionsCantBeReverted,1229 );12301231 target_collection.limits = new_limits;12321233 target_collection.save()1234 }1235 }1236}12371238impl<T: Config> Module<T> {1239 pub fn create_item_internal(1240 sender: &T::CrossAccountId,1241 collection: &CollectionHandle<T>,1242 owner: &T::CrossAccountId,1243 data: CreateItemData,1244 ) -> DispatchResult {1245 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1246 Self::validate_create_item_args(collection, &data)?;1247 Self::create_item_no_validation(collection, owner, data)?;12481249 Ok(())1250 }12511252 pub fn transfer_internal(1253 sender: &T::CrossAccountId,1254 recipient: &T::CrossAccountId,1255 target_collection: &CollectionHandle<T>,1256 item_id: TokenId,1257 value: u128,1258 ) -> DispatchResult {1259 target_collection.consume_gas(2000000)?;1260 // Limits check1261 Self::is_correct_transfer(target_collection, recipient)?;12621263 // Transfer permissions check1264 ensure!(1265 Self::is_item_owner(sender, target_collection, item_id)1266 || Self::is_owner_or_admin_permissions(target_collection, sender),1267 Error::<T>::NoPermission1268 );12691270 if target_collection.access == AccessMode::WhiteList {1271 Self::check_white_list(target_collection, sender)?;1272 Self::check_white_list(target_collection, recipient)?;1273 }12741275 match target_collection.mode {1276 CollectionMode::NFT => Self::transfer_nft(1277 target_collection,1278 item_id,1279 sender.clone(),1280 recipient.clone(),1281 )?,1282 CollectionMode::Fungible(_) => {1283 Self::transfer_fungible(target_collection, value, sender, recipient)?1284 }1285 CollectionMode::ReFungible => Self::transfer_refungible(1286 target_collection,1287 item_id,1288 value,1289 sender.clone(),1290 recipient.clone(),1291 )?,1292 _ => (),1293 };12941295 Self::deposit_event(RawEvent::Transfer(1296 target_collection.id,1297 item_id,1298 sender.clone(),1299 recipient.clone(),1300 value,1301 ));13021303 Ok(())1304 }13051306 pub fn approve_internal(1307 sender: &T::CrossAccountId,1308 spender: &T::CrossAccountId,1309 collection: &CollectionHandle<T>,1310 item_id: TokenId,1311 amount: u128,1312 ) -> DispatchResult {1313 collection.consume_gas(2000000)?;1314 Self::token_exists(collection, item_id)?;13151316 // Transfer permissions check1317 let bypasses_limits = collection.limits.owner_can_transfer1318 && Self::is_owner_or_admin_permissions(collection, sender);13191320 let allowance_limit = if bypasses_limits {1321 None1322 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1323 Some(amount)1324 } else {1325 fail!(Error::<T>::NoPermission);1326 };13271328 if collection.access == AccessMode::WhiteList {1329 Self::check_white_list(collection, sender)?;1330 Self::check_white_list(collection, spender)?;1331 }13321333 let allowance: u128 = amount1334 .checked_add(<Allowances<T>>::get(1335 collection.id,1336 (item_id, sender.as_sub(), spender.as_sub()),1337 ))1338 .ok_or(Error::<T>::NumOverflow)?;1339 if let Some(limit) = allowance_limit {1340 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1341 }1342 <Allowances<T>>::insert(1343 collection.id,1344 (item_id, sender.as_sub(), spender.as_sub()),1345 allowance,1346 );13471348 if matches!(collection.mode, CollectionMode::NFT) {1349 // TODO: NFT: only one owner may exist for token in ERC7211350 collection.log(ERC721Events::Approval {1351 owner: *sender.as_eth(),1352 approved: *spender.as_eth(),1353 token_id: item_id.into(),1354 })?;1355 }13561357 if matches!(collection.mode, CollectionMode::Fungible(_)) {1358 // TODO: NFT: only one owner may exist for token in ERC201359 collection.log(ERC20Events::Approval {1360 owner: *sender.as_eth(),1361 spender: *spender.as_eth(),1362 value: allowance.into(),1363 })?;1364 }13651366 Self::deposit_event(RawEvent::Approved(1367 collection.id,1368 item_id,1369 sender.clone(),1370 spender.clone(),1371 allowance,1372 ));1373 Ok(())1374 }13751376 pub fn transfer_from_internal(1377 sender: &T::CrossAccountId,1378 from: &T::CrossAccountId,1379 recipient: &T::CrossAccountId,1380 collection: &CollectionHandle<T>,1381 item_id: TokenId,1382 amount: u128,1383 ) -> DispatchResult {1384 collection.consume_gas(2000000)?;1385 // Check approval1386 let approval: u128 =1387 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13881389 // Limits check1390 Self::is_correct_transfer(collection, recipient)?;13911392 // Transfer permissions check1393 ensure!(1394 approval >= amount1395 || (collection.limits.owner_can_transfer1396 && Self::is_owner_or_admin_permissions(collection, sender)),1397 Error::<T>::NoPermission1398 );13991400 if collection.access == AccessMode::WhiteList {1401 Self::check_white_list(collection, sender)?;1402 Self::check_white_list(collection, recipient)?;1403 }14041405 // Reduce approval by transferred amount or remove if remaining approval drops to 01406 let allowance = approval.saturating_sub(amount);1407 if allowance > 0 {1408 <Allowances<T>>::insert(1409 collection.id,1410 (item_id, from.as_sub(), sender.as_sub()),1411 allowance,1412 );1413 } else {1414 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1415 }14161417 match collection.mode {1418 CollectionMode::NFT => {1419 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1420 }1421 CollectionMode::Fungible(_) => {1422 Self::transfer_fungible(collection, amount, from, recipient)?1423 }1424 CollectionMode::ReFungible => Self::transfer_refungible(1425 collection,1426 item_id,1427 amount,1428 from.clone(),1429 recipient.clone(),1430 )?,1431 _ => (),1432 };14331434 if matches!(collection.mode, CollectionMode::Fungible(_)) {1435 collection.log(ERC20Events::Approval {1436 owner: *from.as_eth(),1437 spender: *sender.as_eth(),1438 value: allowance.into(),1439 })?;1440 }14411442 Ok(())1443 }14441445 pub fn set_variable_meta_data_internal(1446 sender: &T::CrossAccountId,1447 collection: &CollectionHandle<T>,1448 item_id: TokenId,1449 data: Vec<u8>,1450 ) -> DispatchResult {1451 Self::token_exists(collection, item_id)?;14521453 ensure!(1454 CUSTOM_DATA_LIMIT >= data.len() as u32,1455 Error::<T>::TokenVariableDataLimitExceeded1456 );14571458 // Modify permissions check1459 ensure!(1460 Self::is_item_owner(sender, collection, item_id)1461 || Self::is_owner_or_admin_permissions(collection, sender),1462 Error::<T>::NoPermission1463 );14641465 match collection.mode {1466 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1467 CollectionMode::ReFungible => {1468 Self::set_re_fungible_variable_data(collection, item_id, data)?1469 }1470 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1471 _ => fail!(Error::<T>::UnexpectedCollectionType),1472 };14731474 Ok(())1475 }14761477 pub fn create_multiple_items_internal(1478 sender: &T::CrossAccountId,1479 collection: &CollectionHandle<T>,1480 owner: &T::CrossAccountId,1481 items_data: Vec<CreateItemData>,1482 ) -> DispatchResult {1483 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14841485 for data in &items_data {1486 Self::validate_create_item_args(collection, data)?;1487 }1488 for data in &items_data {1489 Self::create_item_no_validation(collection, owner, data.clone())?;1490 }14911492 Ok(())1493 }14941495 pub fn burn_item_internal(1496 sender: &T::CrossAccountId,1497 collection: &CollectionHandle<T>,1498 item_id: TokenId,1499 value: u128,1500 ) -> DispatchResult {1501 ensure!(1502 Self::is_item_owner(sender, collection, item_id)1503 || (collection.limits.owner_can_transfer1504 && Self::is_owner_or_admin_permissions(collection, sender)),1505 Error::<T>::NoPermission1506 );15071508 if collection.access == AccessMode::WhiteList {1509 Self::check_white_list(collection, sender)?;1510 }15111512 match collection.mode {1513 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1514 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1515 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1516 _ => (),1517 };15181519 Ok(())1520 }15211522 pub fn toggle_white_list_internal(1523 sender: &T::CrossAccountId,1524 collection: &CollectionHandle<T>,1525 address: &T::CrossAccountId,1526 whitelisted: bool,1527 ) -> DispatchResult {1528 Self::check_owner_or_admin_permissions(collection, sender)?;15291530 if whitelisted {1531 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1532 } else {1533 <WhiteList<T>>::remove(collection.id, address.as_sub());1534 }15351536 Ok(())1537 }15381539 fn is_correct_transfer(1540 collection: &CollectionHandle<T>,1541 recipient: &T::CrossAccountId,1542 ) -> DispatchResult {1543 let collection_id = collection.id;15441545 // check token limit and account token limit1546 let account_items: u32 =1547 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1548 ensure!(1549 collection.limits.account_token_ownership_limit > account_items,1550 Error::<T>::AccountTokenLimitExceeded1551 );15521553 // preliminary transfer check1554 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15551556 Ok(())1557 }15581559 fn can_create_items_in_collection(1560 collection: &CollectionHandle<T>,1561 sender: &T::CrossAccountId,1562 owner: &T::CrossAccountId,1563 amount: u32,1564 ) -> DispatchResult {1565 let collection_id = collection.id;15661567 // check token limit and account token limit1568 let total_items: u32 = ItemListIndex::get(collection_id)1569 .checked_add(amount)1570 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1571 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1572 as u32)1573 .checked_add(amount)1574 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1575 ensure!(1576 collection.limits.token_limit >= total_items,1577 Error::<T>::CollectionTokenLimitExceeded1578 );1579 ensure!(1580 collection.limits.account_token_ownership_limit >= account_items,1581 Error::<T>::AccountTokenLimitExceeded1582 );15831584 if !Self::is_owner_or_admin_permissions(collection, sender) {1585 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1586 Self::check_white_list(collection, owner)?;1587 Self::check_white_list(collection, sender)?;1588 }15891590 Ok(())1591 }15921593 fn validate_create_item_args(1594 target_collection: &CollectionHandle<T>,1595 data: &CreateItemData,1596 ) -> DispatchResult {1597 match target_collection.mode {1598 CollectionMode::NFT => {1599 if let CreateItemData::NFT(data) = data {1600 // check sizes1601 ensure!(1602 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1603 Error::<T>::TokenConstDataLimitExceeded1604 );1605 ensure!(1606 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1607 Error::<T>::TokenVariableDataLimitExceeded1608 );1609 } else {1610 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1611 }1612 }1613 CollectionMode::Fungible(_) => {1614 if let CreateItemData::Fungible(_) = data {1615 } else {1616 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1617 }1618 }1619 CollectionMode::ReFungible => {1620 if let CreateItemData::ReFungible(data) = data {1621 // check sizes1622 ensure!(1623 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1624 Error::<T>::TokenConstDataLimitExceeded1625 );1626 ensure!(1627 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1628 Error::<T>::TokenVariableDataLimitExceeded1629 );16301631 // Check refungibility limits1632 ensure!(1633 data.pieces <= MAX_REFUNGIBLE_PIECES,1634 Error::<T>::WrongRefungiblePieces1635 );1636 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1637 } else {1638 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1639 }1640 }1641 _ => {1642 fail!(Error::<T>::UnexpectedCollectionType);1643 }1644 };16451646 Ok(())1647 }16481649 fn create_item_no_validation(1650 collection: &CollectionHandle<T>,1651 owner: &T::CrossAccountId,1652 data: CreateItemData,1653 ) -> DispatchResult {1654 match data {1655 CreateItemData::NFT(data) => {1656 let item = NftItemType {1657 owner: owner.clone(),1658 const_data: data.const_data.into_inner(),1659 variable_data: data.variable_data.into_inner(),1660 };16611662 Self::add_nft_item(collection, item)?;1663 }1664 CreateItemData::Fungible(data) => {1665 Self::add_fungible_item(collection, owner, data.value)?;1666 }1667 CreateItemData::ReFungible(data) => {1668 let owner_list = vec![Ownership {1669 owner: owner.clone(),1670 fraction: data.pieces,1671 }];16721673 let item = ReFungibleItemType {1674 owner: owner_list,1675 const_data: data.const_data.into_inner(),1676 variable_data: data.variable_data.into_inner(),1677 };16781679 Self::add_refungible_item(collection, item)?;1680 }1681 };16821683 Ok(())1684 }16851686 fn add_fungible_item(1687 collection: &CollectionHandle<T>,1688 owner: &T::CrossAccountId,1689 value: u128,1690 ) -> DispatchResult {1691 let collection_id = collection.id;16921693 // Does new owner already have an account?1694 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16951696 // Mint1697 let item = FungibleItemType {1698 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1699 };1700 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17011702 // Update balance1703 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1704 .checked_add(value)1705 .ok_or(Error::<T>::NumOverflow)?;1706 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17071708 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1709 Ok(())1710 }17111712 fn add_refungible_item(1713 collection: &CollectionHandle<T>,1714 item: ReFungibleItemType<T::CrossAccountId>,1715 ) -> DispatchResult {1716 let collection_id = collection.id;17171718 let current_index = <ItemListIndex>::get(collection_id)1719 .checked_add(1)1720 .ok_or(Error::<T>::NumOverflow)?;1721 let itemcopy = item.clone();17221723 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1724 let item_owner = item.owner.first().expect("only one owner is defined");17251726 let value = item_owner.fraction;1727 let owner = item_owner.owner.clone();17281729 Self::add_token_index(collection_id, current_index, &owner)?;17301731 <ItemListIndex>::insert(collection_id, current_index);1732 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17331734 // Update balance1735 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1736 .checked_add(value)1737 .ok_or(Error::<T>::NumOverflow)?;1738 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17391740 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1741 Ok(())1742 }17431744 fn add_nft_item(1745 collection: &CollectionHandle<T>,1746 item: NftItemType<T::CrossAccountId>,1747 ) -> DispatchResult {1748 let collection_id = collection.id;17491750 let current_index = <ItemListIndex>::get(collection_id)1751 .checked_add(1)1752 .ok_or(Error::<T>::NumOverflow)?;17531754 let item_owner = item.owner.clone();1755 Self::add_token_index(collection_id, current_index, &item.owner)?;17561757 <ItemListIndex>::insert(collection_id, current_index);1758 <NftItemList<T>>::insert(collection_id, current_index, item);17591760 // Update balance1761 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1762 .checked_add(1)1763 .ok_or(Error::<T>::NumOverflow)?;1764 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17651766 collection.log(ERC721Events::Transfer {1767 from: H160::default(),1768 to: *item_owner.as_eth(),1769 token_id: current_index.into(),1770 })?;1771 Self::deposit_event(RawEvent::ItemCreated(1772 collection_id,1773 current_index,1774 item_owner,1775 ));1776 Ok(())1777 }17781779 fn burn_refungible_item(1780 collection: &CollectionHandle<T>,1781 item_id: TokenId,1782 owner: &T::CrossAccountId,1783 ) -> DispatchResult {1784 let collection_id = collection.id;17851786 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1787 .ok_or(Error::<T>::TokenNotFound)?;1788 let rft_balance = token1789 .owner1790 .iter()1791 .find(|&i| i.owner == *owner)1792 .ok_or(Error::<T>::TokenNotFound)?;1793 Self::remove_token_index(collection_id, item_id, owner)?;17941795 // update balance1796 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1797 .checked_sub(rft_balance.fraction)1798 .ok_or(Error::<T>::NumOverflow)?;1799 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18001801 // Re-create owners list with sender removed1802 let index = token1803 .owner1804 .iter()1805 .position(|i| i.owner == *owner)1806 .expect("owned item is exists");1807 token.owner.remove(index);1808 let owner_count = token.owner.len();18091810 // Burn the token completely if this was the last (only) owner1811 if owner_count == 0 {1812 <ReFungibleItemList<T>>::remove(collection_id, item_id);1813 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1814 } else {1815 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1816 }18171818 Ok(())1819 }18201821 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1822 let collection_id = collection.id;18231824 let item =1825 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1826 Self::remove_token_index(collection_id, item_id, &item.owner)?;18271828 // update balance1829 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1830 .checked_sub(1)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1833 <NftItemList<T>>::remove(collection_id, item_id);1834 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18351836 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1837 Ok(())1838 }18391840 fn burn_fungible_item(1841 owner: &T::CrossAccountId,1842 collection: &CollectionHandle<T>,1843 value: u128,1844 ) -> DispatchResult {1845 let collection_id = collection.id;18461847 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1848 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18491850 // update balance1851 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1852 .checked_sub(value)1853 .ok_or(Error::<T>::NumOverflow)?;1854 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18551856 if balance.value - value > 0 {1857 balance.value -= value;1858 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1859 } else {1860 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1861 }18621863 collection.log(ERC20Events::Transfer {1864 from: *owner.as_eth(),1865 to: H160::default(),1866 value: value.into(),1867 })?;1868 Ok(())1869 }18701871 pub fn get_collection(1872 collection_id: CollectionId,1873 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1874 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1875 }18761877 fn check_owner_permissions(1878 target_collection: &CollectionHandle<T>,1879 subject: &T::AccountId,1880 ) -> DispatchResult {1881 ensure!(1882 *subject == target_collection.owner,1883 Error::<T>::NoPermission1884 );18851886 Ok(())1887 }18881889 fn is_owner_or_admin_permissions(1890 collection: &CollectionHandle<T>,1891 subject: &T::CrossAccountId,1892 ) -> bool {1893 *subject.as_sub() == collection.owner1894 || <AdminList<T>>::get(collection.id).contains(subject)1895 }18961897 fn check_owner_or_admin_permissions(1898 collection: &CollectionHandle<T>,1899 subject: &T::CrossAccountId,1900 ) -> DispatchResult {1901 ensure!(1902 Self::is_owner_or_admin_permissions(collection, subject),1903 Error::<T>::NoPermission1904 );19051906 Ok(())1907 }19081909 fn owned_amount(1910 subject: &T::CrossAccountId,1911 target_collection: &CollectionHandle<T>,1912 item_id: TokenId,1913 ) -> Option<u128> {1914 let collection_id = target_collection.id;19151916 match target_collection.mode {1917 CollectionMode::NFT => {1918 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1919 }1920 CollectionMode::Fungible(_) => {1921 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1922 }1923 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1924 .owner1925 .iter()1926 .find(|i| i.owner == *subject)1927 .map(|i| i.fraction),1928 CollectionMode::Invalid => None,1929 }1930 }19311932 fn is_item_owner(1933 subject: &T::CrossAccountId,1934 target_collection: &CollectionHandle<T>,1935 item_id: TokenId,1936 ) -> bool {1937 match target_collection.mode {1938 CollectionMode::Fungible(_) => true,1939 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1940 }1941 }19421943 fn check_white_list(1944 collection: &CollectionHandle<T>,1945 address: &T::CrossAccountId,1946 ) -> DispatchResult {1947 let collection_id = collection.id;19481949 let mes = Error::<T>::AddresNotInWhiteList;1950 ensure!(1951 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1952 mes1953 );19541955 Ok(())1956 }19571958 /// Check if token exists. In case of Fungible, check if there is an entry for1959 /// the owner in fungible balances double map1960 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1961 let collection_id = target_collection.id;1962 let exists = match target_collection.mode {1963 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1964 CollectionMode::Fungible(_) => true,1965 CollectionMode::ReFungible => {1966 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1967 }1968 _ => false,1969 };19701971 ensure!(exists, Error::<T>::TokenNotFound);1972 Ok(())1973 }19741975 fn transfer_fungible(1976 collection: &CollectionHandle<T>,1977 value: u128,1978 owner: &T::CrossAccountId,1979 recipient: &T::CrossAccountId,1980 ) -> DispatchResult {1981 let collection_id = collection.id;19821983 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1984 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19851986 // Send balance to recipient (updates balanceOf of recipient)1987 Self::add_fungible_item(collection, recipient, value)?;19881989 // update balanceOf of sender1990 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19911992 // Reduce or remove sender1993 if balance.value == value {1994 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1995 } else {1996 balance.value -= value;1997 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1998 }19992000 collection.log(ERC20Events::Transfer {2001 from: *owner.as_eth(),2002 to: *recipient.as_eth(),2003 value: value.into(),2004 })?;2005 Self::deposit_event(RawEvent::Transfer(2006 collection.id,2007 1,2008 owner.clone(),2009 recipient.clone(),2010 value,2011 ));20122013 Ok(())2014 }20152016 fn transfer_refungible(2017 collection: &CollectionHandle<T>,2018 item_id: TokenId,2019 value: u128,2020 owner: T::CrossAccountId,2021 new_owner: T::CrossAccountId,2022 ) -> DispatchResult {2023 let collection_id = collection.id;2024 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2025 .ok_or(Error::<T>::TokenNotFound)?;20262027 let item = full_item2028 .owner2029 .iter()2030 .find(|i| i.owner == owner)2031 .ok_or(Error::<T>::TokenNotFound)?;2032 let amount = item.fraction;20332034 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20352036 // update balance2037 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2038 .checked_sub(value)2039 .ok_or(Error::<T>::NumOverflow)?;2040 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20412042 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2043 .checked_add(value)2044 .ok_or(Error::<T>::NumOverflow)?;2045 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20462047 let old_owner = item.owner.clone();2048 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20492050 let mut new_full_item = full_item.clone();2051 // transfer2052 if amount == value && !new_owner_has_account {2053 // change owner2054 // new owner do not have account2055 new_full_item2056 .owner2057 .iter_mut()2058 .find(|i| i.owner == owner)2059 .expect("old owner does present in refungible")2060 .owner = new_owner.clone();2061 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20622063 // update index collection2064 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2065 } else {2066 new_full_item2067 .owner2068 .iter_mut()2069 .find(|i| i.owner == owner)2070 .expect("old owner does present in refungible")2071 .fraction -= value;20722073 // separate amount2074 if new_owner_has_account {2075 // new owner has account2076 new_full_item2077 .owner2078 .iter_mut()2079 .find(|i| i.owner == new_owner)2080 .expect("new owner has account")2081 .fraction += value;2082 } else {2083 // new owner do not have account2084 new_full_item.owner.push(Ownership {2085 owner: new_owner.clone(),2086 fraction: value,2087 });2088 Self::add_token_index(collection_id, item_id, &new_owner)?;2089 }20902091 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2092 }20932094 Self::deposit_event(RawEvent::Transfer(2095 collection.id,2096 item_id,2097 owner,2098 new_owner,2099 amount,2100 ));21012102 Ok(())2103 }21042105 fn transfer_nft(2106 collection: &CollectionHandle<T>,2107 item_id: TokenId,2108 sender: T::CrossAccountId,2109 new_owner: T::CrossAccountId,2110 ) -> DispatchResult {2111 let collection_id = collection.id;2112 let mut item =2113 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21142115 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21162117 // update balance2118 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2119 .checked_sub(1)2120 .ok_or(Error::<T>::NumOverflow)?;2121 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21222123 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2124 .checked_add(1)2125 .ok_or(Error::<T>::NumOverflow)?;2126 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21272128 // change owner2129 let old_owner = item.owner.clone();2130 item.owner = new_owner.clone();2131 <NftItemList<T>>::insert(collection_id, item_id, item);21322133 // update index collection2134 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21352136 collection.log(ERC721Events::Transfer {2137 from: *sender.as_eth(),2138 to: *new_owner.as_eth(),2139 token_id: item_id.into(),2140 })?;2141 Self::deposit_event(RawEvent::Transfer(2142 collection.id,2143 item_id,2144 sender,2145 new_owner,2146 1,2147 ));21482149 Ok(())2150 }21512152 fn set_re_fungible_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 = <ReFungibleItemList<T>>::get(collection_id, item_id)2159 .ok_or(Error::<T>::TokenNotFound)?;21602161 item.variable_data = data;21622163 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21642165 Ok(())2166 }21672168 fn set_nft_variable_data(2169 collection: &CollectionHandle<T>,2170 item_id: TokenId,2171 data: Vec<u8>,2172 ) -> DispatchResult {2173 let collection_id = collection.id;2174 let mut item =2175 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21762177 item.variable_data = data;21782179 <NftItemList<T>>::insert(collection_id, item_id, item);21802181 Ok(())2182 }21832184 #[allow(dead_code)]2185 fn init_collection(item: &Collection<T>) {2186 // check params2187 assert!(2188 item.decimal_points <= MAX_DECIMAL_POINTS,2189 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2190 );2191 assert!(2192 item.name.len() <= 64,2193 "Collection name can not be longer than 63 char"2194 );2195 assert!(2196 item.name.len() <= 256,2197 "Collection description can not be longer than 255 char"2198 );2199 assert!(2200 item.token_prefix.len() <= 16,2201 "Token prefix can not be longer than 15 char"2202 );22032204 // Generate next collection ID2205 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22062207 CreatedCollectionCount::put(next_id);2208 }22092210 #[allow(dead_code)]2211 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2212 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22132214 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22152216 <ItemListIndex>::insert(collection_id, current_index);22172218 // Update balance2219 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2220 .checked_add(1)2221 .unwrap();2222 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2223 }22242225 #[allow(dead_code)]2226 fn init_fungible_token(2227 collection_id: CollectionId,2228 owner: &T::CrossAccountId,2229 item: &FungibleItemType,2230 ) {2231 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22322233 Self::add_token_index(collection_id, current_index, owner).unwrap();22342235 <ItemListIndex>::insert(collection_id, current_index);22362237 // Update balance2238 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2239 .checked_add(item.value)2240 .unwrap();2241 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2242 }22432244 #[allow(dead_code)]2245 fn init_refungible_token(2246 collection_id: CollectionId,2247 item: &ReFungibleItemType<T::CrossAccountId>,2248 ) {2249 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22502251 let value = item.owner.first().unwrap().fraction;2252 let owner = item.owner.first().unwrap().owner.clone();22532254 Self::add_token_index(collection_id, current_index, &owner).unwrap();22552256 <ItemListIndex>::insert(collection_id, current_index);22572258 // Update balance2259 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2260 .checked_add(value)2261 .unwrap();2262 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2263 }22642265 fn add_token_index(2266 collection_id: CollectionId,2267 item_index: TokenId,2268 owner: &T::CrossAccountId,2269 ) -> DispatchResult {2270 // add to account limit2271 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2272 // bound Owned tokens by a single address2273 let count = <AccountItemCount<T>>::get(owner.as_sub());2274 ensure!(2275 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2276 Error::<T>::AddressOwnershipLimitExceeded2277 );22782279 <AccountItemCount<T>>::insert(2280 owner.as_sub(),2281 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2282 );2283 } else {2284 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2285 }22862287 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2288 if list_exists {2289 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2290 let item_contains = list.contains(&item_index.clone());22912292 if !item_contains {2293 list.push(item_index);2294 }22952296 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2297 } else {2298 let itm = vec![item_index];2299 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2300 }23012302 Ok(())2303 }23042305 fn remove_token_index(2306 collection_id: CollectionId,2307 item_index: TokenId,2308 owner: &T::CrossAccountId,2309 ) -> DispatchResult {2310 // update counter2311 <AccountItemCount<T>>::insert(2312 owner.as_sub(),2313 <AccountItemCount<T>>::get(owner.as_sub())2314 .checked_sub(1)2315 .ok_or(Error::<T>::NumOverflow)?,2316 );23172318 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2319 if list_exists {2320 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2321 let item_contains = list.contains(&item_index.clone());23222323 if item_contains {2324 list.retain(|&item| item != item_index);2325 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2326 }2327 }23282329 Ok(())2330 }23312332 fn move_token_index(2333 collection_id: CollectionId,2334 item_index: TokenId,2335 old_owner: &T::CrossAccountId,2336 new_owner: &T::CrossAccountId,2337 ) -> DispatchResult {2338 Self::remove_token_index(collection_id, item_index, old_owner)?;2339 Self::add_token_index(collection_id, item_index, new_owner)?;23402341 Ok(())2342 }2343}23442345sp_api::decl_runtime_apis! {2346 pub trait NftApi {2347 /// Used for ethereum integration2348 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2349 }2350}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, AccessMode, Collection, CreateItemData, CollectionLimits, CollectionId,44 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,45 FungibleItemType, ReFungibleItemType,46};4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;55mod eth;56mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;58pub use eth::sponsoring::NftEthSponsorshipHandler;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364#[cfg(feature = "runtime-benchmarks")]65mod benchmarking;6667pub trait WeightInfo {68 fn create_collection() -> Weight;69 fn destroy_collection() -> Weight;70 fn add_to_white_list() -> Weight;71 fn remove_from_white_list() -> Weight;72 fn set_public_access_mode() -> Weight;73 fn set_mint_permission() -> Weight;74 fn change_collection_owner() -> Weight;75 fn add_collection_admin() -> Weight;76 fn remove_collection_admin() -> Weight;77 fn set_collection_sponsor() -> Weight;78 fn confirm_sponsorship() -> Weight;79 fn remove_collection_sponsor() -> Weight;80 fn create_item(s: usize) -> Weight;81 fn burn_item() -> Weight;82 fn transfer() -> Weight;83 fn approve() -> Weight;84 fn transfer_from() -> Weight;85 fn set_offchain_schema() -> Weight;86 fn set_const_on_chain_schema() -> Weight;87 fn set_variable_on_chain_schema() -> Weight;88 fn set_variable_meta_data() -> Weight;89 fn enable_contract_sponsoring() -> Weight;90 fn set_schema_version() -> Weight;91 fn set_contract_sponsoring_rate_limit() -> Weight;92 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;93 fn toggle_contract_white_list() -> Weight;94 fn add_to_contract_white_list() -> Weight;95 fn remove_from_contract_white_list() -> Weight;96 fn set_collection_limits() -> Weight;97}9899decl_error! {100 /// Error for non-fungible-token module.101 pub enum Error for Module<T: Config> {102 /// Total collections bound exceeded.103 TotalCollectionsLimitExceeded,104 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.105 CollectionDecimalPointLimitExceeded,106 /// Collection name can not be longer than 63 char.107 CollectionNameLimitExceeded,108 /// Collection description can not be longer than 255 char.109 CollectionDescriptionLimitExceeded,110 /// Token prefix can not be longer than 15 char.111 CollectionTokenPrefixLimitExceeded,112 /// This collection does not exist.113 CollectionNotFound,114 /// Item not exists.115 TokenNotFound,116 /// Admin not found117 AdminNotFound,118 /// Arithmetic calculation overflow.119 NumOverflow,120 /// Account already has admin role.121 AlreadyAdmin,122 /// You do not own this collection.123 NoPermission,124 /// This address is not set as sponsor, use setCollectionSponsor first.125 ConfirmUnsetSponsorFail,126 /// Collection is not in mint mode.127 PublicMintingNotAllowed,128 /// Sender parameter and item owner must be equal.129 MustBeTokenOwner,130 /// Item balance not enough.131 TokenValueTooLow,132 /// Size of item is too large.133 NftSizeLimitExceeded,134 /// No approve found135 ApproveNotFound,136 /// Requested value more than approved.137 TokenValueNotEnough,138 /// Only approved addresses can call this method.139 ApproveRequired,140 /// Address is not in white list.141 AddresNotInWhiteList,142 /// Number of collection admins bound exceeded.143 CollectionAdminsLimitExceeded,144 /// Owned tokens by a single address bound exceeded.145 AddressOwnershipLimitExceeded,146 /// Length of items properties must be greater than 0.147 EmptyArgument,148 /// const_data exceeded data limit.149 TokenConstDataLimitExceeded,150 /// variable_data exceeded data limit.151 TokenVariableDataLimitExceeded,152 /// Not NFT item data used to mint in NFT collection.153 NotNftDataUsedToMintNftCollectionToken,154 /// Not Fungible item data used to mint in Fungible collection.155 NotFungibleDataUsedToMintFungibleCollectionToken,156 /// Not Re Fungible item data used to mint in Re Fungible collection.157 NotReFungibleDataUsedToMintReFungibleCollectionToken,158 /// Unexpected collection type.159 UnexpectedCollectionType,160 /// Can't store metadata in fungible tokens.161 CantStoreMetadataInFungibleTokens,162 /// Collection token limit exceeded163 CollectionTokenLimitExceeded,164 /// Account token limit exceeded per collection165 AccountTokenLimitExceeded,166 /// Collection limit bounds per collection exceeded167 CollectionLimitBoundsExceeded,168 /// Tried to enable permissions which are only permitted to be disabled169 OwnerPermissionsCantBeReverted,170 /// Schema data size limit bound exceeded171 SchemaDataLimitExceeded,172 /// Maximum refungibility exceeded173 WrongRefungiblePieces,174 /// createRefungible should be called with one owner175 BadCreateRefungibleCall,176 /// Gas limit exceeded177 OutOfGas,178 /// Collection settings not allowing items transferring179 TransferNotAllowed,180 }181}182183#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]184pub struct CollectionHandle<T: Config> {185 pub id: CollectionId,186 collection: Collection<T>,187 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,188}189impl<T: Config> CollectionHandle<T> {190 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {191 <CollectionById<T>>::get(id).map(|collection| Self {192 id,193 collection,194 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(195 eth::collection_id_to_address(id),196 gas_limit,197 ),198 })199 }200 pub fn get(id: CollectionId) -> Option<Self> {201 Self::get_with_gas_limit(id, u64::MAX)202 }203 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {204 self.recorder.log_sub(log)205 }206 fn consume_gas(&self, gas: u64) -> DispatchResult {207 self.recorder.consume_gas_sub(gas)208 }209 pub fn submit_logs(self) -> DispatchResult {210 self.recorder.submit_logs()211 }212 pub fn save(self) -> DispatchResult {213 self.recorder.submit_logs()?;214 <CollectionById<T>>::insert(self.id, self.collection);215 Ok(())216 }217}218impl<T: Config> Deref for CollectionHandle<T> {219 type Target = Collection<T>;220221 fn deref(&self) -> &Self::Target {222 &self.collection223 }224}225226impl<T: Config> DerefMut for CollectionHandle<T> {227 fn deref_mut(&mut self) -> &mut Self::Target {228 &mut self.collection229 }230}231232pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {233 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;234235 /// Weight information for extrinsics in this pallet.236 type WeightInfo: WeightInfo;237238 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;239 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;240241 type CrossAccountId: CrossAccountId<Self::AccountId>;242 type Currency: Currency<Self::AccountId>;243 type CollectionCreationPrice: Get<244 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,245 >;246 type TreasuryAccountId: Get<Self::AccountId>;247}248249// # Used definitions250//251// ## User control levels252//253// chain-controlled - key is uncontrolled by user254// i.e autoincrementing index255// can use non-cryptographic hash256// real - key is controlled by user257// but it is hard to generate enough colliding values, i.e owner of signed txs258// can use non-cryptographic hash259// controlled - key is completly controlled by users260// i.e maps with mutable keys261// should use cryptographic hash262//263// ## User control level downgrade reasons264//265// ?1 - chain-controlled -> controlled266// collections/tokens can be destroyed, resulting in massive holes267// ?2 - chain-controlled -> controlled268// same as ?1, but can be only added, resulting in easier exploitation269// ?3 - real -> controlled270// no confirmation required, so addresses can be easily generated271decl_storage! {272 trait Store for Module<T: Config> as Nft {273274 //#region Private members275 /// Id of next collection276 CreatedCollectionCount: u32;277 /// Used for migrations278 ChainVersion: u64;279 /// Id of last collection token280 /// Collection id (controlled?1)281 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;282 //#endregion283284 //#region Bound counters285 /// Amount of collections destroyed, used for total amount tracking with286 /// CreatedCollectionCount287 DestroyedCollectionCount: u32;288 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)289 /// Account id (real)290 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;291 //#endregion292293 //#region Basic collections294 /// Collection info295 /// Collection id (controlled?1)296 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;297 /// List of collection admins298 /// Collection id (controlled?2)299 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;300 /// Whitelisted collection users301 /// Collection id (controlled?2), user id (controlled?3)302 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;303 //#endregion304305 /// How many of collection items user have306 /// Collection id (controlled?2), account id (real)307 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;308309 /// Amount of items which spender can transfer out of owners account (via transferFrom)310 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))311 /// TODO: Off chain worker should remove from this map when token gets removed312 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;313314 //#region Item collections315 /// Collection id (controlled?2), token id (controlled?1)316 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;317 /// Collection id (controlled?2), owner (controlled?2)318 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;319 /// Collection id (controlled?2), token id (controlled?1)320 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;321 //#endregion322323 //#region Index list324 /// Collection id (controlled?2), tokens owner (controlled?2)325 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;326 //#endregion327328 //#region Tokens transfer rate limit baskets329 /// (Collection id (controlled?2), who created (real))330 /// TODO: Off chain worker should remove from this map when collection gets removed331 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;332 /// Collection id (controlled?2), token id (controlled?2)333 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;334 /// Collection id (controlled?2), owning user (real)335 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;336 /// Collection id (controlled?2), token id (controlled?2)337 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;338 //#endregion339340 /// Variable metadata sponsoring341 /// Collection id (controlled?2), token id (controlled?2)342 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;343 }344 add_extra_genesis {345 build(|config: &GenesisConfig<T>| {346 // Modification of storage347 for (_num, _c) in &config.collection_id {348 <Module<T>>::init_collection(_c);349 }350351 for (_num, _c, _i) in &config.nft_item_id {352 <Module<T>>::init_nft_token(*_c, _i);353 }354355 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {356 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);357 }358359 for (_num, _c, _i) in &config.refungible_item_id {360 <Module<T>>::init_refungible_token(*_c, _i);361 }362 })363 }364}365366decl_event!(367 pub enum Event<T>368 where369 AccountId = <T as frame_system::Config>::AccountId,370 CrossAccountId = <T as Config>::CrossAccountId,371 {372 /// New collection was created373 ///374 /// # Arguments375 ///376 /// * collection_id: Globally unique identifier of newly created collection.377 ///378 /// * mode: [CollectionMode] converted into u8.379 ///380 /// * account_id: Collection owner.381 CollectionCreated(CollectionId, u8, AccountId),382383 /// New item was created.384 ///385 /// # Arguments386 ///387 /// * collection_id: Id of the collection where item was created.388 ///389 /// * item_id: Id of an item. Unique within the collection.390 ///391 /// * recipient: Owner of newly created item392 ItemCreated(CollectionId, TokenId, CrossAccountId),393394 /// Collection item was burned.395 ///396 /// # Arguments397 ///398 /// collection_id.399 ///400 /// item_id: Identifier of burned NFT.401 ItemDestroyed(CollectionId, TokenId),402403 /// Item was transferred404 ///405 /// * collection_id: Id of collection to which item is belong406 ///407 /// * item_id: Id of an item408 ///409 /// * sender: Original owner of item410 ///411 /// * recipient: New owner of item412 ///413 /// * amount: Always 1 for NFT414 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),415416 /// * collection_id417 ///418 /// * item_id419 ///420 /// * sender421 ///422 /// * spender423 ///424 /// * amount425 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),426 }427);428429decl_module! {430 pub struct Module<T: Config> for enum Call431 where432 origin: T::Origin433 {434 fn deposit_event() = default;435 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;436 type Error = Error<T>;437438 fn on_initialize(_now: T::BlockNumber) -> Weight {439 0440 }441442 /// This method creates a Collection of NFTs. Each Token may have multiple properties encoded as an array of bytes of certain length. The initial owner and admin of the collection are set to the address that signed the transaction. Both addresses can be changed later.443 ///444 /// # Permissions445 ///446 /// * Anyone.447 ///448 /// # Arguments449 ///450 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.451 ///452 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.453 ///454 /// * token_prefix: UTF-8 string with token prefix.455 ///456 /// * mode: [CollectionMode] collection type and type dependent data.457 // returns collection ID458 #[weight = <T as Config>::WeightInfo::create_collection()]459 #[transactional]460 pub fn create_collection(origin,461 collection_name: Vec<u16>,462 collection_description: Vec<u16>,463 token_prefix: Vec<u8>,464 mode: CollectionMode) -> DispatchResult {465466 // Anyone can create a collection467 let who = ensure_signed(origin)?;468469 // Take a (non-refundable) deposit of collection creation470 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();471 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(472 &T::TreasuryAccountId::get(),473 T::CollectionCreationPrice::get(),474 ));475 <T as Config>::Currency::settle(476 &who,477 imbalance,478 WithdrawReasons::TRANSFER,479 ExistenceRequirement::KeepAlive,480 ).map_err(|_| Error::<T>::NoPermission)?;481482 let decimal_points = match mode {483 CollectionMode::Fungible(points) => points,484 _ => 0485 };486487 let created_count = CreatedCollectionCount::get();488 let destroyed_count = DestroyedCollectionCount::get();489490 // bound Total number of collections491 ensure!(created_count - destroyed_count < COLLECTION_NUMBER_LIMIT, Error::<T>::TotalCollectionsLimitExceeded);492493 // check params494 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);495 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);496 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);497 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);498499 // Generate next collection ID500 let next_id = created_count501 .checked_add(1)502 .ok_or(Error::<T>::NumOverflow)?;503504 CreatedCollectionCount::put(next_id);505506 let limits = CollectionLimits {507 sponsored_data_size: CUSTOM_DATA_LIMIT,508 ..Default::default()509 };510511 // Create new collection512 let new_collection = Collection {513 owner: who.clone(),514 name: collection_name,515 mode: mode.clone(),516 mint_mode: false,517 access: AccessMode::Normal,518 description: collection_description,519 decimal_points,520 token_prefix,521 offchain_schema: Vec::new(),522 schema_version: SchemaVersion::ImageURL,523 sponsorship: SponsorshipState::Disabled,524 variable_on_chain_schema: Vec::new(),525 const_on_chain_schema: Vec::new(),526 limits,527 transfers_enabled: true,528 };529530 // Add new collection to map531 <CollectionById<T>>::insert(next_id, new_collection);532533 // call event534 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));535536 Ok(())537 }538539 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.540 ///541 /// # Permissions542 ///543 /// * Collection Owner.544 ///545 /// # Arguments546 ///547 /// * collection_id: collection to destroy.548 #[weight = <T as Config>::WeightInfo::destroy_collection()]549 #[transactional]550 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {551552 let sender = ensure_signed(origin)?;553 let collection = Self::get_collection(collection_id)?;554 Self::check_owner_permissions(&collection, &sender)?;555 if !collection.limits.owner_can_destroy {556 fail!(Error::<T>::NoPermission);557 }558559 <AddressTokens<T>>::remove_prefix(collection_id, None);560 <Allowances<T>>::remove_prefix(collection_id, None);561 <Balance<T>>::remove_prefix(collection_id, None);562 <ItemListIndex>::remove(collection_id);563 <AdminList<T>>::remove(collection_id);564 <CollectionById<T>>::remove(collection_id);565 <WhiteList<T>>::remove_prefix(collection_id, None);566567 <NftItemList<T>>::remove_prefix(collection_id, None);568 <FungibleItemList<T>>::remove_prefix(collection_id, None);569 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);570571 <NftTransferBasket<T>>::remove_prefix(collection_id, None);572 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);573 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);574575 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);576577 DestroyedCollectionCount::put(DestroyedCollectionCount::get()578 .checked_add(1)579 .ok_or(Error::<T>::NumOverflow)?);580581 Ok(())582 }583584 /// Add an address to white list.585 ///586 /// # Permissions587 ///588 /// * Collection Owner589 /// * Collection Admin590 ///591 /// # Arguments592 ///593 /// * collection_id.594 ///595 /// * address.596 #[weight = <T as Config>::WeightInfo::add_to_white_list()]597 #[transactional]598 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{599600 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);601 let collection = Self::get_collection(collection_id)?;602603 Self::toggle_white_list_internal(604 &sender,605 &collection,606 &address,607 true,608 )?;609610 Ok(())611 }612613 /// Remove an address from white list.614 ///615 /// # Permissions616 ///617 /// * Collection Owner618 /// * Collection Admin619 ///620 /// # Arguments621 ///622 /// * collection_id.623 ///624 /// * address.625 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]626 #[transactional]627 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{628629 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);630 let collection = Self::get_collection(collection_id)?;631632 Self::toggle_white_list_internal(633 &sender,634 &collection,635 &address,636 false,637 )?;638639 Ok(())640 }641642 /// Toggle between normal and white list access for the methods with access for `Anyone`.643 ///644 /// # Permissions645 ///646 /// * Collection Owner.647 ///648 /// # Arguments649 ///650 /// * collection_id.651 ///652 /// * mode: [AccessMode]653 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]654 #[transactional]655 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult656 {657 let sender = ensure_signed(origin)?;658659 let mut target_collection = Self::get_collection(collection_id)?;660 Self::check_owner_permissions(&target_collection, &sender)?;661 target_collection.access = mode;662 target_collection.save()663 }664665 /// Allows Anyone to create tokens if:666 /// * White List is enabled, and667 /// * Address is added to white list, and668 /// * This method was called with True parameter669 ///670 /// # Permissions671 /// * Collection Owner672 ///673 /// # Arguments674 ///675 /// * collection_id.676 ///677 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.678 #[weight = <T as Config>::WeightInfo::set_mint_permission()]679 #[transactional]680 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult681 {682 let sender = ensure_signed(origin)?;683684 let mut target_collection = Self::get_collection(collection_id)?;685 Self::check_owner_permissions(&target_collection, &sender)?;686 target_collection.mint_mode = mint_permission;687 target_collection.save()688 }689690 /// Change the owner of the collection.691 ///692 /// # Permissions693 ///694 /// * Collection Owner.695 ///696 /// # Arguments697 ///698 /// * collection_id.699 ///700 /// * new_owner.701 #[weight = <T as Config>::WeightInfo::change_collection_owner()]702 #[transactional]703 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {704705 let sender = ensure_signed(origin)?;706 let mut target_collection = Self::get_collection(collection_id)?;707 Self::check_owner_permissions(&target_collection, &sender)?;708 target_collection.owner = new_owner;709 target_collection.save()710 }711712 /// Adds an admin of the Collection.713 /// NFT Collection can be controlled by multiple admin addresses (some which can also be servers, for example). Admins can issue and burn NFTs, as well as add and remove other admins, but cannot change NFT or Collection ownership.714 ///715 /// # Permissions716 ///717 /// * Collection Owner.718 /// * Collection Admin.719 ///720 /// # Arguments721 ///722 /// * collection_id: ID of the Collection to add admin for.723 ///724 /// * new_admin_id: Address of new admin to add.725 #[weight = <T as Config>::WeightInfo::add_collection_admin()]726 #[transactional]727 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {728 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);729 let collection = Self::get_collection(collection_id)?;730 Self::check_owner_or_admin_permissions(&collection, &sender)?;731 let mut admin_arr = <AdminList<T>>::get(collection_id);732733 match admin_arr.binary_search(&new_admin_id) {734 Ok(_) => {},735 Err(idx) => {736 ensure!(admin_arr.len() < COLLECTION_ADMINS_LIMIT as usize, Error::<T>::CollectionAdminsLimitExceeded);737 admin_arr.insert(idx, new_admin_id);738 <AdminList<T>>::insert(collection_id, admin_arr);739 }740 }741 Ok(())742 }743744 /// Remove admin address of the Collection. An admin address can remove itself. List of admins may become empty, in which case only Collection Owner will be able to add an Admin.745 ///746 /// # Permissions747 ///748 /// * Collection Owner.749 /// * Collection Admin.750 ///751 /// # Arguments752 ///753 /// * collection_id: ID of the Collection to remove admin for.754 ///755 /// * account_id: Address of admin to remove.756 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]757 #[transactional]758 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {759 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);760 let collection = Self::get_collection(collection_id)?;761 Self::check_owner_or_admin_permissions(&collection, &sender)?;762 let mut admin_arr = <AdminList<T>>::get(collection_id);763764 if let Ok(idx) = admin_arr.binary_search(&account_id) {765 admin_arr.remove(idx);766 <AdminList<T>>::insert(collection_id, admin_arr);767 }768 Ok(())769 }770771 /// # Permissions772 ///773 /// * Collection Owner774 ///775 /// # Arguments776 ///777 /// * collection_id.778 ///779 /// * new_sponsor.780 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]781 #[transactional]782 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {783 let sender = ensure_signed(origin)?;784 let mut target_collection = Self::get_collection(collection_id)?;785 Self::check_owner_permissions(&target_collection, &sender)?;786787 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);788 target_collection.save()789 }790791 /// # Permissions792 ///793 /// * Sponsor.794 ///795 /// # Arguments796 ///797 /// * collection_id.798 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]799 #[transactional]800 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {801 let sender = ensure_signed(origin)?;802803 let mut target_collection = Self::get_collection(collection_id)?;804 ensure!(805 target_collection.sponsorship.pending_sponsor() == Some(&sender),806 Error::<T>::ConfirmUnsetSponsorFail807 );808809 target_collection.sponsorship = SponsorshipState::Confirmed(sender);810 target_collection.save()811 }812813 /// Switch back to pay-per-own-transaction model.814 ///815 /// # Permissions816 ///817 /// * Collection owner.818 ///819 /// # Arguments820 ///821 /// * collection_id.822 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]823 #[transactional]824 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {825 let sender = ensure_signed(origin)?;826827 let mut target_collection = Self::get_collection(collection_id)?;828 Self::check_owner_permissions(&target_collection, &sender)?;829830 target_collection.sponsorship = SponsorshipState::Disabled;831 target_collection.save()832 }833834 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.835 ///836 /// # Permissions837 ///838 /// * Collection Owner.839 /// * Collection Admin.840 /// * Anyone if841 /// * White List is enabled, and842 /// * Address is added to white list, and843 /// * MintPermission is enabled (see SetMintPermission method)844 ///845 /// # Arguments846 ///847 /// * collection_id: ID of the collection.848 ///849 /// * owner: Address, initial owner of the NFT.850 ///851 /// * data: Token data to store on chain.852 // #[weight =853 // (130_000_000 as Weight)854 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))855 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))856 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]857858 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]859 #[transactional]860 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {861 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);862 let collection = Self::get_collection(collection_id)?;863864 Self::create_item_internal(&sender, &collection, &owner, data)?;865866 collection.submit_logs()867 }868869 /// This method creates multiple items in a collection created with CreateCollection method.870 ///871 /// # Permissions872 ///873 /// * Collection Owner.874 /// * Collection Admin.875 /// * Anyone if876 /// * White List is enabled, and877 /// * Address is added to white list, and878 /// * MintPermission is enabled (see SetMintPermission method)879 ///880 /// # Arguments881 ///882 /// * collection_id: ID of the collection.883 ///884 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].885 ///886 /// * owner: Address, initial owner of the NFT.887 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()888 .map(|data| { data.data_size() })889 .sum())]890 #[transactional]891 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {892893 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);894 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);895 let collection = Self::get_collection(collection_id)?;896897 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;898899 collection.submit_logs()900 }901902 // TODO! transaction weight903904 /// Set transfers_enabled value for particular collection905 ///906 /// # Permissions907 ///908 /// * Collection Owner.909 ///910 /// # Arguments911 ///912 /// * collection_id: ID of the collection.913 ///914 /// * value: New flag value.915 #[weight = <T as Config>::WeightInfo::burn_item()]916 #[transactional]917 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {918919 let sender = ensure_signed(origin)?;920 let mut target_collection = Self::get_collection(collection_id)?;921922 Self::check_owner_permissions(&target_collection, &sender)?;923924 target_collection.transfers_enabled = value;925 target_collection.save()926 }927928 /// Destroys a concrete instance of NFT.929 ///930 /// # Permissions931 ///932 /// * Collection Owner.933 /// * Collection Admin.934 /// * Current NFT Owner.935 ///936 /// # Arguments937 ///938 /// * collection_id: ID of the collection.939 ///940 /// * item_id: ID of NFT to burn.941 #[weight = <T as Config>::WeightInfo::burn_item()]942 #[transactional]943 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {944945 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);946 let target_collection = Self::get_collection(collection_id)?;947948 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;949950 target_collection.submit_logs()951 }952953 /// Change ownership of the token.954 ///955 /// # Permissions956 ///957 /// * Collection Owner958 /// * Collection Admin959 /// * Current NFT owner960 ///961 /// # Arguments962 ///963 /// * recipient: Address of token recipient.964 ///965 /// * collection_id.966 ///967 /// * item_id: ID of the item968 /// * Non-Fungible Mode: Required.969 /// * Fungible Mode: Ignored.970 /// * Re-Fungible Mode: Required.971 ///972 /// * value: Amount to transfer.973 /// * Non-Fungible Mode: Ignored974 /// * Fungible Mode: Must specify transferred amount975 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)976 #[weight = <T as Config>::WeightInfo::transfer()]977 #[transactional]978 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {979 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);980 let collection = Self::get_collection(collection_id)?;981982 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;983984 collection.submit_logs()985 }986987 /// Set, change, or remove approved address to transfer the ownership of the NFT.988 ///989 /// # Permissions990 ///991 /// * Collection Owner992 /// * Collection Admin993 /// * Current NFT owner994 ///995 /// # Arguments996 ///997 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).998 ///999 /// * collection_id.1000 ///1001 /// * item_id: ID of the item.1002 #[weight = <T as Config>::WeightInfo::approve()]1003 #[transactional]1004 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1005 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006 let collection = Self::get_collection(collection_id)?;10071008 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10091010 collection.submit_logs()1011 }10121013 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.1014 ///1015 /// # Permissions1016 /// * Collection Owner1017 /// * Collection Admin1018 /// * Current NFT owner1019 /// * Address approved by current NFT owner1020 ///1021 /// # Arguments1022 ///1023 /// * from: Address that owns token.1024 ///1025 /// * recipient: Address of token recipient.1026 ///1027 /// * collection_id.1028 ///1029 /// * item_id: ID of the item.1030 ///1031 /// * value: Amount to transfer.1032 #[weight = <T as Config>::WeightInfo::transfer_from()]1033 #[transactional]1034 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1035 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1036 let collection = Self::get_collection(collection_id)?;10371038 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10391040 collection.submit_logs()1041 }1042 // #[weight = 0]1043 // // let no_perm_mes = "You do not have permissions to modify this collection";1044 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1045 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1046 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10471048 // // // on_nft_received call10491050 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10511052 // Ok(())1053 // }10541055 /// Set off-chain data schema.1056 ///1057 /// # Permissions1058 ///1059 /// * Collection Owner1060 /// * Collection Admin1061 ///1062 /// # Arguments1063 ///1064 /// * collection_id.1065 ///1066 /// * schema: String representing the offchain data schema.1067 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1068 #[transactional]1069 pub fn set_variable_meta_data (1070 origin,1071 collection_id: CollectionId,1072 item_id: TokenId,1073 data: Vec<u8>1074 ) -> DispatchResult {1075 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10761077 let collection = Self::get_collection(collection_id)?;10781079 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10801081 Ok(())1082 }10831084 /// Set schema standard1085 /// ImageURL1086 /// Unique1087 ///1088 /// # Permissions1089 ///1090 /// * Collection Owner1091 /// * Collection Admin1092 ///1093 /// # Arguments1094 ///1095 /// * collection_id.1096 ///1097 /// * schema: SchemaVersion: enum1098 #[weight = <T as Config>::WeightInfo::set_schema_version()]1099 #[transactional]1100 pub fn set_schema_version(1101 origin,1102 collection_id: CollectionId,1103 version: SchemaVersion1104 ) -> DispatchResult {1105 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1106 let mut target_collection = Self::get_collection(collection_id)?;1107 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1108 target_collection.schema_version = version;1109 target_collection.save()1110 }11111112 /// Set off-chain data schema.1113 ///1114 /// # Permissions1115 ///1116 /// * Collection Owner1117 /// * Collection Admin1118 ///1119 /// # Arguments1120 ///1121 /// * collection_id.1122 ///1123 /// * schema: String representing the offchain data schema.1124 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1125 #[transactional]1126 pub fn set_offchain_schema(1127 origin,1128 collection_id: CollectionId,1129 schema: Vec<u8>1130 ) -> DispatchResult {1131 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1132 let mut target_collection = Self::get_collection(collection_id)?;1133 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11341135 // check schema limit1136 ensure!(schema.len() as u32 <= OFFCHAIN_SCHEMA_LIMIT, "");11371138 target_collection.offchain_schema = schema;1139 target_collection.save()1140 }11411142 /// Set const on-chain data schema.1143 ///1144 /// # Permissions1145 ///1146 /// * Collection Owner1147 /// * Collection Admin1148 ///1149 /// # Arguments1150 ///1151 /// * collection_id.1152 ///1153 /// * schema: String representing the const on-chain data schema.1154 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1155 #[transactional]1156 pub fn set_const_on_chain_schema (1157 origin,1158 collection_id: CollectionId,1159 schema: Vec<u8>1160 ) -> DispatchResult {1161 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1162 let mut target_collection = Self::get_collection(collection_id)?;1163 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11641165 // check schema limit1166 ensure!(schema.len() as u32 <= CONST_ON_CHAIN_SCHEMA_LIMIT, "");11671168 target_collection.const_on_chain_schema = schema;1169 target_collection.save()1170 }11711172 /// Set variable on-chain data schema.1173 ///1174 /// # Permissions1175 ///1176 /// * Collection Owner1177 /// * Collection Admin1178 ///1179 /// # Arguments1180 ///1181 /// * collection_id.1182 ///1183 /// * schema: String representing the variable on-chain data schema.1184 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1185 #[transactional]1186 pub fn set_variable_on_chain_schema (1187 origin,1188 collection_id: CollectionId,1189 schema: Vec<u8>1190 ) -> DispatchResult {1191 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1192 let mut target_collection = Self::get_collection(collection_id)?;1193 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11941195 // check schema limit1196 ensure!(schema.len() as u32 <= VARIABLE_ON_CHAIN_SCHEMA_LIMIT, "");11971198 target_collection.variable_on_chain_schema = schema;1199 target_collection.save()1200 }12011202 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1203 #[transactional]1204 pub fn set_collection_limits(1205 origin,1206 collection_id: u32,1207 new_limits: CollectionLimits<T::BlockNumber>,1208 ) -> DispatchResult {1209 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1210 let mut target_collection = Self::get_collection(collection_id)?;1211 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1212 let old_limits = &target_collection.limits;12131214 // collection bounds1215 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1216 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1217 new_limits.sponsored_data_size <= CUSTOM_DATA_LIMIT,1218 Error::<T>::CollectionLimitBoundsExceeded);12191220 // token_limit check prev1221 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1222 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12231224 ensure!(1225 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1226 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1227 Error::<T>::OwnerPermissionsCantBeReverted,1228 );12291230 target_collection.limits = new_limits;12311232 target_collection.save()1233 }1234 }1235}12361237impl<T: Config> Module<T> {1238 pub fn create_item_internal(1239 sender: &T::CrossAccountId,1240 collection: &CollectionHandle<T>,1241 owner: &T::CrossAccountId,1242 data: CreateItemData,1243 ) -> DispatchResult {1244 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1245 Self::validate_create_item_args(collection, &data)?;1246 Self::create_item_no_validation(collection, owner, data)?;12471248 Ok(())1249 }12501251 pub fn transfer_internal(1252 sender: &T::CrossAccountId,1253 recipient: &T::CrossAccountId,1254 target_collection: &CollectionHandle<T>,1255 item_id: TokenId,1256 value: u128,1257 ) -> DispatchResult {1258 target_collection.consume_gas(2000000)?;1259 // Limits check1260 Self::is_correct_transfer(target_collection, recipient)?;12611262 // Transfer permissions check1263 ensure!(1264 Self::is_item_owner(sender, target_collection, item_id)1265 || Self::is_owner_or_admin_permissions(target_collection, sender),1266 Error::<T>::NoPermission1267 );12681269 if target_collection.access == AccessMode::WhiteList {1270 Self::check_white_list(target_collection, sender)?;1271 Self::check_white_list(target_collection, recipient)?;1272 }12731274 match target_collection.mode {1275 CollectionMode::NFT => Self::transfer_nft(1276 target_collection,1277 item_id,1278 sender.clone(),1279 recipient.clone(),1280 )?,1281 CollectionMode::Fungible(_) => {1282 Self::transfer_fungible(target_collection, value, sender, recipient)?1283 }1284 CollectionMode::ReFungible => Self::transfer_refungible(1285 target_collection,1286 item_id,1287 value,1288 sender.clone(),1289 recipient.clone(),1290 )?,1291 _ => (),1292 };12931294 Self::deposit_event(RawEvent::Transfer(1295 target_collection.id,1296 item_id,1297 sender.clone(),1298 recipient.clone(),1299 value,1300 ));13011302 Ok(())1303 }13041305 pub fn approve_internal(1306 sender: &T::CrossAccountId,1307 spender: &T::CrossAccountId,1308 collection: &CollectionHandle<T>,1309 item_id: TokenId,1310 amount: u128,1311 ) -> DispatchResult {1312 collection.consume_gas(2000000)?;1313 Self::token_exists(collection, item_id)?;13141315 // Transfer permissions check1316 let bypasses_limits = collection.limits.owner_can_transfer1317 && Self::is_owner_or_admin_permissions(collection, sender);13181319 let allowance_limit = if bypasses_limits {1320 None1321 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1322 Some(amount)1323 } else {1324 fail!(Error::<T>::NoPermission);1325 };13261327 if collection.access == AccessMode::WhiteList {1328 Self::check_white_list(collection, sender)?;1329 Self::check_white_list(collection, spender)?;1330 }13311332 let allowance: u128 = amount1333 .checked_add(<Allowances<T>>::get(1334 collection.id,1335 (item_id, sender.as_sub(), spender.as_sub()),1336 ))1337 .ok_or(Error::<T>::NumOverflow)?;1338 if let Some(limit) = allowance_limit {1339 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1340 }1341 <Allowances<T>>::insert(1342 collection.id,1343 (item_id, sender.as_sub(), spender.as_sub()),1344 allowance,1345 );13461347 if matches!(collection.mode, CollectionMode::NFT) {1348 // TODO: NFT: only one owner may exist for token in ERC7211349 collection.log(ERC721Events::Approval {1350 owner: *sender.as_eth(),1351 approved: *spender.as_eth(),1352 token_id: item_id.into(),1353 })?;1354 }13551356 if matches!(collection.mode, CollectionMode::Fungible(_)) {1357 // TODO: NFT: only one owner may exist for token in ERC201358 collection.log(ERC20Events::Approval {1359 owner: *sender.as_eth(),1360 spender: *spender.as_eth(),1361 value: allowance.into(),1362 })?;1363 }13641365 Self::deposit_event(RawEvent::Approved(1366 collection.id,1367 item_id,1368 sender.clone(),1369 spender.clone(),1370 allowance,1371 ));1372 Ok(())1373 }13741375 pub fn transfer_from_internal(1376 sender: &T::CrossAccountId,1377 from: &T::CrossAccountId,1378 recipient: &T::CrossAccountId,1379 collection: &CollectionHandle<T>,1380 item_id: TokenId,1381 amount: u128,1382 ) -> DispatchResult {1383 collection.consume_gas(2000000)?;1384 // Check approval1385 let approval: u128 =1386 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13871388 // Limits check1389 Self::is_correct_transfer(collection, recipient)?;13901391 // Transfer permissions check1392 ensure!(1393 approval >= amount1394 || (collection.limits.owner_can_transfer1395 && Self::is_owner_or_admin_permissions(collection, sender)),1396 Error::<T>::NoPermission1397 );13981399 if collection.access == AccessMode::WhiteList {1400 Self::check_white_list(collection, sender)?;1401 Self::check_white_list(collection, recipient)?;1402 }14031404 // Reduce approval by transferred amount or remove if remaining approval drops to 01405 let allowance = approval.saturating_sub(amount);1406 if allowance > 0 {1407 <Allowances<T>>::insert(1408 collection.id,1409 (item_id, from.as_sub(), sender.as_sub()),1410 allowance,1411 );1412 } else {1413 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1414 }14151416 match collection.mode {1417 CollectionMode::NFT => {1418 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1419 }1420 CollectionMode::Fungible(_) => {1421 Self::transfer_fungible(collection, amount, from, recipient)?1422 }1423 CollectionMode::ReFungible => Self::transfer_refungible(1424 collection,1425 item_id,1426 amount,1427 from.clone(),1428 recipient.clone(),1429 )?,1430 _ => (),1431 };14321433 if matches!(collection.mode, CollectionMode::Fungible(_)) {1434 collection.log(ERC20Events::Approval {1435 owner: *from.as_eth(),1436 spender: *sender.as_eth(),1437 value: allowance.into(),1438 })?;1439 }14401441 Ok(())1442 }14431444 pub fn set_variable_meta_data_internal(1445 sender: &T::CrossAccountId,1446 collection: &CollectionHandle<T>,1447 item_id: TokenId,1448 data: Vec<u8>,1449 ) -> DispatchResult {1450 Self::token_exists(collection, item_id)?;14511452 ensure!(1453 CUSTOM_DATA_LIMIT >= data.len() as u32,1454 Error::<T>::TokenVariableDataLimitExceeded1455 );14561457 // Modify permissions check1458 ensure!(1459 Self::is_item_owner(sender, collection, item_id)1460 || Self::is_owner_or_admin_permissions(collection, sender),1461 Error::<T>::NoPermission1462 );14631464 match collection.mode {1465 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1466 CollectionMode::ReFungible => {1467 Self::set_re_fungible_variable_data(collection, item_id, data)?1468 }1469 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1470 _ => fail!(Error::<T>::UnexpectedCollectionType),1471 };14721473 Ok(())1474 }14751476 pub fn create_multiple_items_internal(1477 sender: &T::CrossAccountId,1478 collection: &CollectionHandle<T>,1479 owner: &T::CrossAccountId,1480 items_data: Vec<CreateItemData>,1481 ) -> DispatchResult {1482 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14831484 for data in &items_data {1485 Self::validate_create_item_args(collection, data)?;1486 }1487 for data in &items_data {1488 Self::create_item_no_validation(collection, owner, data.clone())?;1489 }14901491 Ok(())1492 }14931494 pub fn burn_item_internal(1495 sender: &T::CrossAccountId,1496 collection: &CollectionHandle<T>,1497 item_id: TokenId,1498 value: u128,1499 ) -> DispatchResult {1500 ensure!(1501 Self::is_item_owner(sender, collection, item_id)1502 || (collection.limits.owner_can_transfer1503 && Self::is_owner_or_admin_permissions(collection, sender)),1504 Error::<T>::NoPermission1505 );15061507 if collection.access == AccessMode::WhiteList {1508 Self::check_white_list(collection, sender)?;1509 }15101511 match collection.mode {1512 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1513 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1514 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1515 _ => (),1516 };15171518 Ok(())1519 }15201521 pub fn toggle_white_list_internal(1522 sender: &T::CrossAccountId,1523 collection: &CollectionHandle<T>,1524 address: &T::CrossAccountId,1525 whitelisted: bool,1526 ) -> DispatchResult {1527 Self::check_owner_or_admin_permissions(collection, sender)?;15281529 if whitelisted {1530 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1531 } else {1532 <WhiteList<T>>::remove(collection.id, address.as_sub());1533 }15341535 Ok(())1536 }15371538 fn is_correct_transfer(1539 collection: &CollectionHandle<T>,1540 recipient: &T::CrossAccountId,1541 ) -> DispatchResult {1542 let collection_id = collection.id;15431544 // check token limit and account token limit1545 let account_items: u32 =1546 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1547 ensure!(1548 collection.limits.account_token_ownership_limit > account_items,1549 Error::<T>::AccountTokenLimitExceeded1550 );15511552 // preliminary transfer check1553 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15541555 Ok(())1556 }15571558 fn can_create_items_in_collection(1559 collection: &CollectionHandle<T>,1560 sender: &T::CrossAccountId,1561 owner: &T::CrossAccountId,1562 amount: u32,1563 ) -> DispatchResult {1564 let collection_id = collection.id;15651566 // check token limit and account token limit1567 let total_items: u32 = ItemListIndex::get(collection_id)1568 .checked_add(amount)1569 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1570 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1571 as u32)1572 .checked_add(amount)1573 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1574 ensure!(1575 collection.limits.token_limit >= total_items,1576 Error::<T>::CollectionTokenLimitExceeded1577 );1578 ensure!(1579 collection.limits.account_token_ownership_limit >= account_items,1580 Error::<T>::AccountTokenLimitExceeded1581 );15821583 if !Self::is_owner_or_admin_permissions(collection, sender) {1584 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1585 Self::check_white_list(collection, owner)?;1586 Self::check_white_list(collection, sender)?;1587 }15881589 Ok(())1590 }15911592 fn validate_create_item_args(1593 target_collection: &CollectionHandle<T>,1594 data: &CreateItemData,1595 ) -> DispatchResult {1596 match target_collection.mode {1597 CollectionMode::NFT => {1598 if !matches!(data, CreateItemData::NFT(_)) {1599 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1600 }1601 }1602 CollectionMode::Fungible(_) => {1603 if !matches!(data, CreateItemData::Fungible(_)) {1604 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1605 }1606 }1607 CollectionMode::ReFungible => {1608 if let CreateItemData::ReFungible(data) = data {1609 // Check refungibility limits1610 ensure!(1611 data.pieces <= MAX_REFUNGIBLE_PIECES,1612 Error::<T>::WrongRefungiblePieces1613 );1614 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1615 } else {1616 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1617 }1618 }1619 _ => {1620 fail!(Error::<T>::UnexpectedCollectionType);1621 }1622 };16231624 Ok(())1625 }16261627 fn create_item_no_validation(1628 collection: &CollectionHandle<T>,1629 owner: &T::CrossAccountId,1630 data: CreateItemData,1631 ) -> DispatchResult {1632 match data {1633 CreateItemData::NFT(data) => {1634 let item = NftItemType {1635 owner: owner.clone(),1636 const_data: data.const_data.into_inner(),1637 variable_data: data.variable_data.into_inner(),1638 };16391640 Self::add_nft_item(collection, item)?;1641 }1642 CreateItemData::Fungible(data) => {1643 Self::add_fungible_item(collection, owner, data.value)?;1644 }1645 CreateItemData::ReFungible(data) => {1646 let owner_list = vec![Ownership {1647 owner: owner.clone(),1648 fraction: data.pieces,1649 }];16501651 let item = ReFungibleItemType {1652 owner: owner_list,1653 const_data: data.const_data.into_inner(),1654 variable_data: data.variable_data.into_inner(),1655 };16561657 Self::add_refungible_item(collection, item)?;1658 }1659 };16601661 Ok(())1662 }16631664 fn add_fungible_item(1665 collection: &CollectionHandle<T>,1666 owner: &T::CrossAccountId,1667 value: u128,1668 ) -> DispatchResult {1669 let collection_id = collection.id;16701671 // Does new owner already have an account?1672 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16731674 // Mint1675 let item = FungibleItemType {1676 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1677 };1678 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);16791680 // Update balance1681 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1682 .checked_add(value)1683 .ok_or(Error::<T>::NumOverflow)?;1684 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);16851686 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1687 Ok(())1688 }16891690 fn add_refungible_item(1691 collection: &CollectionHandle<T>,1692 item: ReFungibleItemType<T::CrossAccountId>,1693 ) -> DispatchResult {1694 let collection_id = collection.id;16951696 let current_index = <ItemListIndex>::get(collection_id)1697 .checked_add(1)1698 .ok_or(Error::<T>::NumOverflow)?;1699 let itemcopy = item.clone();17001701 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1702 let item_owner = item.owner.first().expect("only one owner is defined");17031704 let value = item_owner.fraction;1705 let owner = item_owner.owner.clone();17061707 Self::add_token_index(collection_id, current_index, &owner)?;17081709 <ItemListIndex>::insert(collection_id, current_index);1710 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17111712 // Update balance1713 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1714 .checked_add(value)1715 .ok_or(Error::<T>::NumOverflow)?;1716 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17171718 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1719 Ok(())1720 }17211722 fn add_nft_item(1723 collection: &CollectionHandle<T>,1724 item: NftItemType<T::CrossAccountId>,1725 ) -> DispatchResult {1726 let collection_id = collection.id;17271728 let current_index = <ItemListIndex>::get(collection_id)1729 .checked_add(1)1730 .ok_or(Error::<T>::NumOverflow)?;17311732 let item_owner = item.owner.clone();1733 Self::add_token_index(collection_id, current_index, &item.owner)?;17341735 <ItemListIndex>::insert(collection_id, current_index);1736 <NftItemList<T>>::insert(collection_id, current_index, item);17371738 // Update balance1739 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1740 .checked_add(1)1741 .ok_or(Error::<T>::NumOverflow)?;1742 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17431744 collection.log(ERC721Events::Transfer {1745 from: H160::default(),1746 to: *item_owner.as_eth(),1747 token_id: current_index.into(),1748 })?;1749 Self::deposit_event(RawEvent::ItemCreated(1750 collection_id,1751 current_index,1752 item_owner,1753 ));1754 Ok(())1755 }17561757 fn burn_refungible_item(1758 collection: &CollectionHandle<T>,1759 item_id: TokenId,1760 owner: &T::CrossAccountId,1761 ) -> DispatchResult {1762 let collection_id = collection.id;17631764 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1765 .ok_or(Error::<T>::TokenNotFound)?;1766 let rft_balance = token1767 .owner1768 .iter()1769 .find(|&i| i.owner == *owner)1770 .ok_or(Error::<T>::TokenNotFound)?;1771 Self::remove_token_index(collection_id, item_id, owner)?;17721773 // update balance1774 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1775 .checked_sub(rft_balance.fraction)1776 .ok_or(Error::<T>::NumOverflow)?;1777 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17781779 // Re-create owners list with sender removed1780 let index = token1781 .owner1782 .iter()1783 .position(|i| i.owner == *owner)1784 .expect("owned item is exists");1785 token.owner.remove(index);1786 let owner_count = token.owner.len();17871788 // Burn the token completely if this was the last (only) owner1789 if owner_count == 0 {1790 <ReFungibleItemList<T>>::remove(collection_id, item_id);1791 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1792 } else {1793 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1794 }17951796 Ok(())1797 }17981799 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1800 let collection_id = collection.id;18011802 let item =1803 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1804 Self::remove_token_index(collection_id, item_id, &item.owner)?;18051806 // update balance1807 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1808 .checked_sub(1)1809 .ok_or(Error::<T>::NumOverflow)?;1810 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1811 <NftItemList<T>>::remove(collection_id, item_id);1812 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18131814 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1815 Ok(())1816 }18171818 fn burn_fungible_item(1819 owner: &T::CrossAccountId,1820 collection: &CollectionHandle<T>,1821 value: u128,1822 ) -> DispatchResult {1823 let collection_id = collection.id;18241825 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1826 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18271828 // update balance1829 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1830 .checked_sub(value)1831 .ok_or(Error::<T>::NumOverflow)?;1832 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18331834 if balance.value - value > 0 {1835 balance.value -= value;1836 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1837 } else {1838 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1839 }18401841 collection.log(ERC20Events::Transfer {1842 from: *owner.as_eth(),1843 to: H160::default(),1844 value: value.into(),1845 })?;1846 Ok(())1847 }18481849 pub fn get_collection(1850 collection_id: CollectionId,1851 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1852 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1853 }18541855 fn check_owner_permissions(1856 target_collection: &CollectionHandle<T>,1857 subject: &T::AccountId,1858 ) -> DispatchResult {1859 ensure!(1860 *subject == target_collection.owner,1861 Error::<T>::NoPermission1862 );18631864 Ok(())1865 }18661867 fn is_owner_or_admin_permissions(1868 collection: &CollectionHandle<T>,1869 subject: &T::CrossAccountId,1870 ) -> bool {1871 *subject.as_sub() == collection.owner1872 || <AdminList<T>>::get(collection.id).contains(subject)1873 }18741875 fn check_owner_or_admin_permissions(1876 collection: &CollectionHandle<T>,1877 subject: &T::CrossAccountId,1878 ) -> DispatchResult {1879 ensure!(1880 Self::is_owner_or_admin_permissions(collection, subject),1881 Error::<T>::NoPermission1882 );18831884 Ok(())1885 }18861887 fn owned_amount(1888 subject: &T::CrossAccountId,1889 target_collection: &CollectionHandle<T>,1890 item_id: TokenId,1891 ) -> Option<u128> {1892 let collection_id = target_collection.id;18931894 match target_collection.mode {1895 CollectionMode::NFT => {1896 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1897 }1898 CollectionMode::Fungible(_) => {1899 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1900 }1901 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1902 .owner1903 .iter()1904 .find(|i| i.owner == *subject)1905 .map(|i| i.fraction),1906 CollectionMode::Invalid => None,1907 }1908 }19091910 fn is_item_owner(1911 subject: &T::CrossAccountId,1912 target_collection: &CollectionHandle<T>,1913 item_id: TokenId,1914 ) -> bool {1915 match target_collection.mode {1916 CollectionMode::Fungible(_) => true,1917 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1918 }1919 }19201921 fn check_white_list(1922 collection: &CollectionHandle<T>,1923 address: &T::CrossAccountId,1924 ) -> DispatchResult {1925 let collection_id = collection.id;19261927 let mes = Error::<T>::AddresNotInWhiteList;1928 ensure!(1929 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1930 mes1931 );19321933 Ok(())1934 }19351936 /// Check if token exists. In case of Fungible, check if there is an entry for1937 /// the owner in fungible balances double map1938 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1939 let collection_id = target_collection.id;1940 let exists = match target_collection.mode {1941 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1942 CollectionMode::Fungible(_) => true,1943 CollectionMode::ReFungible => {1944 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1945 }1946 _ => false,1947 };19481949 ensure!(exists, Error::<T>::TokenNotFound);1950 Ok(())1951 }19521953 fn transfer_fungible(1954 collection: &CollectionHandle<T>,1955 value: u128,1956 owner: &T::CrossAccountId,1957 recipient: &T::CrossAccountId,1958 ) -> DispatchResult {1959 let collection_id = collection.id;19601961 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1962 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19631964 // Send balance to recipient (updates balanceOf of recipient)1965 Self::add_fungible_item(collection, recipient, value)?;19661967 // update balanceOf of sender1968 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19691970 // Reduce or remove sender1971 if balance.value == value {1972 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1973 } else {1974 balance.value -= value;1975 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1976 }19771978 collection.log(ERC20Events::Transfer {1979 from: *owner.as_eth(),1980 to: *recipient.as_eth(),1981 value: value.into(),1982 })?;1983 Self::deposit_event(RawEvent::Transfer(1984 collection.id,1985 1,1986 owner.clone(),1987 recipient.clone(),1988 value,1989 ));19901991 Ok(())1992 }19931994 fn transfer_refungible(1995 collection: &CollectionHandle<T>,1996 item_id: TokenId,1997 value: u128,1998 owner: T::CrossAccountId,1999 new_owner: T::CrossAccountId,2000 ) -> DispatchResult {2001 let collection_id = collection.id;2002 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2003 .ok_or(Error::<T>::TokenNotFound)?;20042005 let item = full_item2006 .owner2007 .iter()2008 .find(|i| i.owner == owner)2009 .ok_or(Error::<T>::TokenNotFound)?;2010 let amount = item.fraction;20112012 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20132014 // update balance2015 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2016 .checked_sub(value)2017 .ok_or(Error::<T>::NumOverflow)?;2018 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20192020 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2021 .checked_add(value)2022 .ok_or(Error::<T>::NumOverflow)?;2023 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20242025 let old_owner = item.owner.clone();2026 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20272028 let mut new_full_item = full_item.clone();2029 // transfer2030 if amount == value && !new_owner_has_account {2031 // change owner2032 // new owner do not have account2033 new_full_item2034 .owner2035 .iter_mut()2036 .find(|i| i.owner == owner)2037 .expect("old owner does present in refungible")2038 .owner = new_owner.clone();2039 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20402041 // update index collection2042 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2043 } else {2044 new_full_item2045 .owner2046 .iter_mut()2047 .find(|i| i.owner == owner)2048 .expect("old owner does present in refungible")2049 .fraction -= value;20502051 // separate amount2052 if new_owner_has_account {2053 // new owner has account2054 new_full_item2055 .owner2056 .iter_mut()2057 .find(|i| i.owner == new_owner)2058 .expect("new owner has account")2059 .fraction += value;2060 } else {2061 // new owner do not have account2062 new_full_item.owner.push(Ownership {2063 owner: new_owner.clone(),2064 fraction: value,2065 });2066 Self::add_token_index(collection_id, item_id, &new_owner)?;2067 }20682069 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2070 }20712072 Self::deposit_event(RawEvent::Transfer(2073 collection.id,2074 item_id,2075 owner,2076 new_owner,2077 amount,2078 ));20792080 Ok(())2081 }20822083 fn transfer_nft(2084 collection: &CollectionHandle<T>,2085 item_id: TokenId,2086 sender: T::CrossAccountId,2087 new_owner: T::CrossAccountId,2088 ) -> DispatchResult {2089 let collection_id = collection.id;2090 let mut item =2091 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;20922093 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);20942095 // update balance2096 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2097 .checked_sub(1)2098 .ok_or(Error::<T>::NumOverflow)?;2099 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21002101 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2102 .checked_add(1)2103 .ok_or(Error::<T>::NumOverflow)?;2104 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21052106 // change owner2107 let old_owner = item.owner.clone();2108 item.owner = new_owner.clone();2109 <NftItemList<T>>::insert(collection_id, item_id, item);21102111 // update index collection2112 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21132114 collection.log(ERC721Events::Transfer {2115 from: *sender.as_eth(),2116 to: *new_owner.as_eth(),2117 token_id: item_id.into(),2118 })?;2119 Self::deposit_event(RawEvent::Transfer(2120 collection.id,2121 item_id,2122 sender,2123 new_owner,2124 1,2125 ));21262127 Ok(())2128 }21292130 fn set_re_fungible_variable_data(2131 collection: &CollectionHandle<T>,2132 item_id: TokenId,2133 data: Vec<u8>,2134 ) -> DispatchResult {2135 let collection_id = collection.id;2136 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2137 .ok_or(Error::<T>::TokenNotFound)?;21382139 item.variable_data = data;21402141 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21422143 Ok(())2144 }21452146 fn set_nft_variable_data(2147 collection: &CollectionHandle<T>,2148 item_id: TokenId,2149 data: Vec<u8>,2150 ) -> DispatchResult {2151 let collection_id = collection.id;2152 let mut item =2153 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21542155 item.variable_data = data;21562157 <NftItemList<T>>::insert(collection_id, item_id, item);21582159 Ok(())2160 }21612162 #[allow(dead_code)]2163 fn init_collection(item: &Collection<T>) {2164 // check params2165 assert!(2166 item.decimal_points <= MAX_DECIMAL_POINTS,2167 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2168 );2169 assert!(2170 item.name.len() <= 64,2171 "Collection name can not be longer than 63 char"2172 );2173 assert!(2174 item.name.len() <= 256,2175 "Collection description can not be longer than 255 char"2176 );2177 assert!(2178 item.token_prefix.len() <= 16,2179 "Token prefix can not be longer than 15 char"2180 );21812182 // Generate next collection ID2183 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();21842185 CreatedCollectionCount::put(next_id);2186 }21872188 #[allow(dead_code)]2189 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2190 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();21912192 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();21932194 <ItemListIndex>::insert(collection_id, current_index);21952196 // Update balance2197 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2198 .checked_add(1)2199 .unwrap();2200 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2201 }22022203 #[allow(dead_code)]2204 fn init_fungible_token(2205 collection_id: CollectionId,2206 owner: &T::CrossAccountId,2207 item: &FungibleItemType,2208 ) {2209 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22102211 Self::add_token_index(collection_id, current_index, owner).unwrap();22122213 <ItemListIndex>::insert(collection_id, current_index);22142215 // Update balance2216 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2217 .checked_add(item.value)2218 .unwrap();2219 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2220 }22212222 #[allow(dead_code)]2223 fn init_refungible_token(2224 collection_id: CollectionId,2225 item: &ReFungibleItemType<T::CrossAccountId>,2226 ) {2227 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22282229 let value = item.owner.first().unwrap().fraction;2230 let owner = item.owner.first().unwrap().owner.clone();22312232 Self::add_token_index(collection_id, current_index, &owner).unwrap();22332234 <ItemListIndex>::insert(collection_id, current_index);22352236 // Update balance2237 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2238 .checked_add(value)2239 .unwrap();2240 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241 }22422243 fn add_token_index(2244 collection_id: CollectionId,2245 item_index: TokenId,2246 owner: &T::CrossAccountId,2247 ) -> DispatchResult {2248 // add to account limit2249 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2250 // bound Owned tokens by a single address2251 let count = <AccountItemCount<T>>::get(owner.as_sub());2252 ensure!(2253 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2254 Error::<T>::AddressOwnershipLimitExceeded2255 );22562257 <AccountItemCount<T>>::insert(2258 owner.as_sub(),2259 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2260 );2261 } else {2262 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2263 }22642265 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2266 if list_exists {2267 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2268 let item_contains = list.contains(&item_index.clone());22692270 if !item_contains {2271 list.push(item_index);2272 }22732274 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2275 } else {2276 let itm = vec![item_index];2277 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2278 }22792280 Ok(())2281 }22822283 fn remove_token_index(2284 collection_id: CollectionId,2285 item_index: TokenId,2286 owner: &T::CrossAccountId,2287 ) -> DispatchResult {2288 // update counter2289 <AccountItemCount<T>>::insert(2290 owner.as_sub(),2291 <AccountItemCount<T>>::get(owner.as_sub())2292 .checked_sub(1)2293 .ok_or(Error::<T>::NumOverflow)?,2294 );22952296 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2297 if list_exists {2298 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2299 let item_contains = list.contains(&item_index.clone());23002301 if item_contains {2302 list.retain(|&item| item != item_index);2303 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2304 }2305 }23062307 Ok(())2308 }23092310 fn move_token_index(2311 collection_id: CollectionId,2312 item_index: TokenId,2313 old_owner: &T::CrossAccountId,2314 new_owner: &T::CrossAccountId,2315 ) -> DispatchResult {2316 Self::remove_token_index(collection_id, item_index, old_owner)?;2317 Self::add_token_index(collection_id, item_index, new_owner)?;23182319 Ok(())2320 }2321}23222323sp_api::decl_runtime_apis! {2324 pub trait NftApi {2325 /// Used for ethereum integration2326 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2327 }2328}pallets/nft/src/tests.rsdiffbeforeafterboth--- a/pallets/nft/src/tests.rs
+++ b/pallets/nft/src/tests.rs
@@ -1,40 +1,18 @@
// Tests to be written here
use super::*;
use crate::mock::*;
-use crate::{AccessMode, CollectionMode, Ownership, ChainLimits, CreateItemData};
+use crate::{AccessMode, CollectionMode, Ownership, CreateItemData};
use nft_data_structs::{
CreateNftData, CreateFungibleData, CreateReFungibleData, CollectionId, TokenId,
MAX_DECIMAL_POINTS,
};
use frame_support::{assert_noop, assert_ok};
-use frame_system::{RawOrigin};
-
-fn default_collection_numbers_limit() -> u32 {
- 10
-}
-
-fn default_limits() {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: default_collection_numbers_limit(),
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-}
+use sp_std::convert::TryInto;
fn default_nft_data() -> CreateNftData {
CreateNftData {
- const_data: vec![1, 2, 3],
- variable_data: vec![3, 2, 1],
+ const_data: vec![1, 2, 3].try_into().unwrap(),
+ variable_data: vec![3, 2, 1].try_into().unwrap(),
}
}
@@ -44,8 +22,8 @@
fn default_re_fungible_data() -> CreateReFungibleData {
CreateReFungibleData {
- const_data: vec![1, 2, 3],
- variable_data: vec![3, 2, 1],
+ const_data: vec![1, 2, 3].try_into().unwrap(),
+ variable_data: vec![3, 2, 1].try_into().unwrap(),
pieces: 1023,
}
}
@@ -112,7 +90,6 @@
#[test]
fn set_version_schema() {
new_test_ext().execute_with(|| {
- default_limits();
let origin1 = Origin::signed(1);
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -133,8 +110,6 @@
#[test]
fn create_fungible_collection_fails_with_large_decimal_numbers() {
new_test_ext().execute_with(|| {
- default_limits();
-
let col_name1: Vec<u16> = "Test1\0".encode_utf16().collect::<Vec<u16>>();
let col_desc1: Vec<u16> = "TestDescription1\0".encode_utf16().collect::<Vec<u16>>();
let token_prefix1: Vec<u8> = b"token_prefix1\0".to_vec();
@@ -156,14 +131,13 @@
#[test]
fn create_nft_item() {
new_test_ext().execute_with(|| {
- default_limits();
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
create_test_item(collection_id, &data.clone().into());
let item = TemplateModule::nft_item_id(collection_id, 1).unwrap();
- assert_eq!(item.const_data, data.const_data);
- assert_eq!(item.variable_data, data.variable_data);
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
});
}
@@ -172,8 +146,6 @@
#[test]
fn create_nft_multiple_items() {
new_test_ext().execute_with(|| {
- default_limits();
-
create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -190,10 +162,10 @@
.map(|d| { d.into() })
.collect()
));
- for (index, data) in items_data.iter().enumerate() {
+ for (index, data) in items_data.into_iter().enumerate() {
let item = TemplateModule::nft_item_id(1, (index + 1) as TokenId).unwrap();
- assert_eq!(item.const_data.to_vec(), data.const_data);
- assert_eq!(item.variable_data.to_vec(), data.variable_data);
+ assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+ assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
}
});
}
@@ -201,14 +173,13 @@
#[test]
fn create_refungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let data = default_re_fungible_data();
create_test_item(collection_id, &data.clone().into());
let item = TemplateModule::refungible_item_id(collection_id, 1).unwrap();
- assert_eq!(item.const_data, data.const_data);
- assert_eq!(item.variable_data, data.variable_data);
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
assert_eq!(
item.owner[0],
Ownership {
@@ -222,8 +193,6 @@
#[test]
fn create_multiple_refungible_items() {
new_test_ext().execute_with(|| {
- default_limits();
-
create_test_collection(&CollectionMode::ReFungible, 1);
let origin1 = Origin::signed(1);
@@ -244,10 +213,10 @@
.map(|d| { d.into() })
.collect()
));
- for (index, data) in items_data.iter().enumerate() {
+ for (index, data) in items_data.into_iter().enumerate() {
let item = TemplateModule::refungible_item_id(1, (index + 1) as TokenId).unwrap();
- assert_eq!(item.const_data.to_vec(), data.const_data);
- assert_eq!(item.variable_data.to_vec(), data.variable_data);
+ assert_eq!(item.const_data.to_vec(), data.const_data.into_inner());
+ assert_eq!(item.variable_data.to_vec(), data.variable_data.into_inner());
assert_eq!(
item.owner[0],
Ownership {
@@ -262,8 +231,6 @@
#[test]
fn create_fungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let data = default_fungible_data();
@@ -302,8 +269,6 @@
#[test]
fn transfer_fungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let origin1 = Origin::signed(1);
@@ -344,8 +309,6 @@
#[test]
fn transfer_refungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let data = default_re_fungible_data();
@@ -355,8 +318,8 @@
let origin2 = Origin::signed(2);
{
let item = TemplateModule::refungible_item_id(collection_id, 1).unwrap();
- assert_eq!(item.const_data, data.const_data);
- assert_eq!(item.variable_data, data.variable_data);
+ assert_eq!(item.const_data, data.const_data.into_inner());
+ assert_eq!(item.variable_data, data.variable_data.into_inner());
assert_eq!(
item.owner[0],
Ownership {
@@ -441,8 +404,6 @@
#[test]
fn transfer_nft_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -464,8 +425,6 @@
#[test]
fn nft_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -503,8 +462,6 @@
#[test]
fn nft_approve_and_transfer_from_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -514,8 +471,8 @@
create_test_item(collection_id, &data.clone().into());
assert_eq!(
- TemplateModule::nft_item_id(1, 1).unwrap().const_data,
- data.const_data
+ &TemplateModule::nft_item_id(1, 1).unwrap().const_data,
+ &data.const_data.into_inner()
);
assert_eq!(TemplateModule::balance_count(1, 1), 1);
assert_eq!(TemplateModule::address_tokens(1, 1), [1]);
@@ -573,8 +530,6 @@
#[test]
fn refungible_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let origin1 = Origin::signed(1);
@@ -636,8 +591,6 @@
#[test]
fn fungible_approve_and_transfer_from() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let data = default_fungible_data();
@@ -710,8 +663,6 @@
#[test]
fn change_collection_owner() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -730,8 +681,6 @@
#[test]
fn destroy_collection() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -742,8 +691,6 @@
#[test]
fn burn_nft_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -773,8 +720,6 @@
#[test]
fn burn_fungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let origin1 = Origin::signed(1);
@@ -804,8 +749,6 @@
#[test]
fn burn_refungible_item() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let origin1 = Origin::signed(1);
@@ -851,8 +794,6 @@
#[test]
fn add_collection_admin() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
@@ -879,8 +820,6 @@
#[test]
fn remove_collection_admin() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection1_id = create_test_collection_for_owner(&CollectionMode::NFT, 1, 1);
create_test_collection_for_owner(&CollectionMode::NFT, 2, 2);
create_test_collection_for_owner(&CollectionMode::NFT, 3, 3);
@@ -916,8 +855,6 @@
#[test]
fn balance_of() {
new_test_ext().execute_with(|| {
- default_limits();
-
let nft_collection_id = create_test_collection(&CollectionMode::NFT, 1);
let fungible_collection_id = create_test_collection(&CollectionMode::Fungible(3), 2);
let re_fungible_collection_id = create_test_collection(&CollectionMode::ReFungible, 3);
@@ -969,8 +906,6 @@
#[test]
fn approve() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -987,8 +922,6 @@
#[test]
fn transfer_from() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
@@ -1051,8 +984,6 @@
#[test]
fn owner_can_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1068,8 +999,6 @@
#[test]
fn admin_can_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
@@ -1091,8 +1020,6 @@
#[test]
fn nonprivileged_user_cannot_add_address_to_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin2 = Origin::signed(2);
@@ -1106,8 +1033,6 @@
#[test]
fn nobody_can_add_address_to_white_list_of_nonexisting_collection() {
new_test_ext().execute_with(|| {
- default_limits();
-
let origin1 = Origin::signed(1);
assert_noop!(
@@ -1120,8 +1045,6 @@
#[test]
fn nobody_can_add_address_to_white_list_of_deleted_collection() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1140,8 +1063,6 @@
#[test]
fn address_is_already_added_to_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1162,8 +1083,6 @@
#[test]
fn owner_can_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1184,8 +1103,6 @@
#[test]
fn admin_can_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
@@ -1213,8 +1130,6 @@
#[test]
fn nonprivileged_user_cannot_remove_address_from_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
@@ -1235,7 +1150,6 @@
#[test]
fn nobody_can_remove_address_from_white_list_of_nonexisting_collection() {
new_test_ext().execute_with(|| {
- default_limits();
let origin1 = Origin::signed(1);
assert_noop!(
@@ -1248,8 +1162,6 @@
#[test]
fn nobody_can_remove_address_from_white_list_of_deleted_collection() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
let origin2 = Origin::signed(2);
@@ -1272,8 +1184,6 @@
#[test]
fn address_is_already_removed_from_white_list() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1300,8 +1210,6 @@
#[test]
fn white_list_test_1() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1330,8 +1238,6 @@
#[test]
fn white_list_test_2() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1381,8 +1287,6 @@
#[test]
fn white_list_test_3() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1411,8 +1315,6 @@
#[test]
fn white_list_test_4() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1463,8 +1365,6 @@
#[test]
fn white_list_test_5() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1488,8 +1388,6 @@
#[test]
fn white_list_test_6() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1516,8 +1414,6 @@
#[test]
fn white_list_test_7() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -1548,8 +1444,6 @@
#[test]
fn white_list_test_8() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -1598,8 +1492,6 @@
#[test]
fn white_list_test_9() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1623,8 +1515,6 @@
#[test]
fn white_list_test_10() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1660,8 +1550,6 @@
#[test]
fn white_list_test_11() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1694,8 +1582,6 @@
#[test]
fn white_list_test_12() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1723,8 +1609,6 @@
#[test]
fn white_list_test_13() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1749,8 +1633,6 @@
#[test]
fn white_list_test_14() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1786,8 +1668,6 @@
#[test]
fn white_list_test_15() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1815,8 +1695,6 @@
#[test]
fn white_list_test_16() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1851,8 +1729,6 @@
#[test]
fn total_number_collections_bound() {
new_test_ext().execute_with(|| {
- default_limits();
-
create_test_collection(&CollectionMode::NFT, 1);
});
}
@@ -1861,11 +1737,9 @@
#[test]
fn total_number_collections_bound_neg() {
new_test_ext().execute_with(|| {
- default_limits();
-
let origin1 = Origin::signed(1);
- for i in 0..default_collection_numbers_limit() {
+ for i in 0..COLLECTION_NUMBER_LIMIT {
create_test_collection(&CollectionMode::NFT, i + 1);
}
@@ -1891,8 +1765,6 @@
#[test]
fn owned_tokens_bound() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let data = default_nft_data();
@@ -1905,28 +1777,16 @@
#[test]
fn owned_tokens_bound_neg() {
new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 1,
- collections_admins_limit: 5,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- let data = default_nft_data();
- create_test_item(collection_id, &data.clone().into());
+ for _ in 0..ACCOUNT_TOKEN_OWNERSHIP_LIMIT {
+ let data = default_nft_data();
+ create_test_item(collection_id, &data.clone().into());
+ }
+
+ let data = default_nft_data();
assert_noop!(
TemplateModule::create_item(origin1, 1, account(1), data.into()),
Error::<Test>::AddressOwnershipLimitExceeded
@@ -1938,22 +1798,6 @@
#[test]
fn collection_admins_bound() {
new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 2,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -1975,174 +1819,20 @@
#[test]
fn collection_admins_bound_neg() {
new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 1,
- collections_admins_limit: 1,
- custom_data_limit: 2048,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
- assert_ok!(TemplateModule::add_collection_admin(
- origin1.clone(),
- collection_id,
- account(2)
- ));
+ for i in 0..COLLECTION_ADMINS_LIMIT {
+ assert_ok!(TemplateModule::add_collection_admin(
+ origin1.clone(),
+ collection_id,
+ account(2 + i)
+ ));
+ }
assert_noop!(
- TemplateModule::add_collection_admin(origin1, collection_id, account(3)),
+ TemplateModule::add_collection_admin(origin1, collection_id, account(3 + COLLECTION_ADMINS_LIMIT)),
Error::<Test>::CollectionAdminsLimitExceeded
- );
- });
-}
-
-// NFT custom data size. Negative test const_data.
-#[test]
-fn custom_data_size_nft_const_data_bound_neg() {
- new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
- let origin1 = Origin::signed(1);
- let too_big_const_data = CreateItemData::NFT(CreateNftData {
- const_data: vec![1, 2, 3, 4],
- variable_data: vec![],
- });
-
- assert_noop!(
- TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
- Error::<Test>::TokenConstDataLimitExceeded
- );
- });
-}
-
-// NFT custom data size. Negative test variable_data.
-#[test]
-fn custom_data_size_nft_variable_data_bound_neg() {
- new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
- let origin1 = Origin::signed(1);
- let too_big_const_data = CreateItemData::NFT(CreateNftData {
- const_data: vec![],
- variable_data: vec![1, 2, 3, 4],
- });
-
- assert_noop!(
- TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
- Error::<Test>::TokenVariableDataLimitExceeded
- );
- });
-}
-
-// Re fungible custom data size. Negative test const_data.
-#[test]
-fn custom_data_size_re_fungible_const_data_bound_neg() {
- new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
- let origin1 = Origin::signed(1);
- let too_big_const_data = CreateItemData::NFT(CreateNftData {
- const_data: vec![1, 2, 3, 4],
- variable_data: vec![],
- });
-
- assert_noop!(
- TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
- Error::<Test>::TokenConstDataLimitExceeded
- );
- });
-}
-
-// Re fungible custom data size. Negative test variable_data.
-#[test]
-fn custom_data_size_re_fungible_variable_data_bound_neg() {
- new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: 10,
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 2,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
- let collection_id = create_test_collection(&CollectionMode::NFT, 1);
-
- let origin1 = Origin::signed(1);
- let too_big_const_data = CreateItemData::NFT(CreateNftData {
- const_data: vec![],
- variable_data: vec![1, 2, 3, 4],
- });
-
- assert_noop!(
- TemplateModule::create_item(origin1, collection_id, account(1), too_big_const_data),
- Error::<Test>::TokenVariableDataLimitExceeded
);
});
}
@@ -2151,8 +1841,6 @@
#[test]
fn set_const_on_chain_schema() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -2180,8 +1868,6 @@
#[test]
fn set_variable_on_chain_schema() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -2209,8 +1895,6 @@
#[test]
fn set_variable_meta_data_on_nft_token_stores_variable_meta_data() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -2218,7 +1902,7 @@
let data = default_nft_data();
create_test_item(1, &data.into());
- let variable_data = b"test set_variable_meta_data method.".to_vec();
+ let variable_data = b"test data".to_vec();
assert_ok!(TemplateModule::set_variable_meta_data(
origin1,
collection_id,
@@ -2238,8 +1922,6 @@
#[test]
fn set_variable_meta_data_on_re_fungible_token_stores_variable_meta_data() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let origin1 = Origin::signed(1);
@@ -2247,7 +1929,7 @@
let data = default_re_fungible_data();
create_test_item(1, &data.into());
- let variable_data = b"test set_variable_meta_data method.".to_vec();
+ let variable_data = b"test data".to_vec();
assert_ok!(TemplateModule::set_variable_meta_data(
origin1,
collection_id,
@@ -2267,8 +1949,6 @@
#[test]
fn set_variable_meta_data_on_fungible_token_fails() {
new_test_ext().execute_with(|| {
- default_limits();
-
let collection_id = create_test_collection(&CollectionMode::Fungible(3), 1);
let origin1 = Origin::signed(1);
@@ -2276,7 +1956,7 @@
let data = default_fungible_data();
create_test_item(1, &data.into());
- let variable_data = b"test set_variable_meta_data method.".to_vec();
+ let variable_data = b"test data".to_vec();
assert_noop!(
TemplateModule::set_variable_meta_data(origin1, collection_id, 1, variable_data),
Error::<Test>::CantStoreMetadataInFungibleTokens
@@ -2287,22 +1967,6 @@
#[test]
fn set_variable_meta_data_on_nft_token_fails_for_big_data() {
new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: default_collection_numbers_limit(),
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 10,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
let origin1 = Origin::signed(1);
@@ -2321,22 +1985,6 @@
#[test]
fn set_variable_meta_data_on_re_fungible_token_fails_for_big_data() {
new_test_ext().execute_with(|| {
- assert_ok!(TemplateModule::set_chain_limits(
- RawOrigin::Root.into(),
- ChainLimits {
- collection_numbers_limit: default_collection_numbers_limit(),
- account_token_ownership_limit: 10,
- collections_admins_limit: 5,
- custom_data_limit: 10,
- nft_sponsor_transfer_timeout: 15,
- fungible_sponsor_transfer_timeout: 15,
- refungible_sponsor_transfer_timeout: 15,
- const_on_chain_schema_limit: 1024,
- offchain_schema_limit: 1024,
- variable_on_chain_schema_limit: 1024,
- }
- ));
-
let collection_id = create_test_collection(&CollectionMode::ReFungible, 1);
let origin1 = Origin::signed(1);
@@ -2355,8 +2003,6 @@
#[test]
fn collection_transfer_flag_works() {
new_test_ext().execute_with(|| {
- default_limits();
-
let origin1 = Origin::signed(1);
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
@@ -2382,8 +2028,6 @@
#[test]
fn collection_transfer_flag_works_neg() {
new_test_ext().execute_with(|| {
- default_limits();
-
let origin1 = Origin::signed(1);
let collection_id = create_test_collection(&CollectionMode::NFT, 1);
primitives/nft/Cargo.tomldiffbeforeafterboth--- a/primitives/nft/Cargo.toml
+++ b/primitives/nft/Cargo.toml
@@ -32,4 +32,5 @@
"sp-core/std",
"sp-std/std",
]
-serde1 = ["serde"]
\ No newline at end of file
+serde1 = ["serde"]
+limit-testing = []
\ No newline at end of file
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -28,10 +28,10 @@
pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;
pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;
-pub const COLLECTION_NUMBER_LIMIT: u32 = 100000;
-pub const CUSTOM_DATA_LIMIT: u32 = 2048;
+pub const COLLECTION_NUMBER_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 100000 } else { 10 };
+pub const CUSTOM_DATA_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 2048 } else { 10 };
pub const COLLECTION_ADMINS_LIMIT: u64 = 5;
-pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = 1000000;
+pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = if cfg!(not(feature = "limit-testing")) { 1000000 } else { 10 };
// Timeouts for item types in passed blocks
pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
runtime/Cargo.tomldiffbeforeafterboth--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -85,6 +85,10 @@
'xcm-builder/std',
'xcm-executor/std',
]
+limit-testing = [
+ 'pallet-nft/limit-testing',
+ 'nft-data-structs/limit-testing',
+]
################################################################################
# Substrate Dependencies
runtime/src/nft_weights.rsdiffbeforeafterboth--- a/runtime/src/nft_weights.rs
+++ b/runtime/src/nft_weights.rs
@@ -123,11 +123,6 @@
.saturating_add(DbWeight::get().reads(2_u64))
.saturating_add(DbWeight::get().writes(1_u64))
}
- fn set_chain_limits() -> Weight {
- 1_300_000_u64
- .saturating_add(DbWeight::get().reads(0_u64))
- .saturating_add(DbWeight::get().writes(1_u64))
- }
fn set_contract_sponsoring_rate_limit() -> Weight {
3_500_000_u64
.saturating_add(DbWeight::get().reads(0_u64))