difftreelog
refactor move ChainLimits to constants
in: master
5 files changed
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -2,12 +2,11 @@
use crate::{
Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,
- eth::{account::EvmBackwardsAddressMapping, map_eth_to_id}, limit,
+ eth::{account::EvmBackwardsAddressMapping, map_eth_to_id},
};
use evm_coder::{Call, abi::AbiReader};
use frame_support::{
storage::{StorageMap, StorageDoubleMap},
- traits::Get,
};
use sp_core::H160;
use sp_std::prelude::*;
@@ -18,6 +17,7 @@
};
use core::convert::TryInto;
use core::marker::PhantomData;
+use nft_data_structs::{NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
struct AnyError;
@@ -44,7 +44,7 @@
let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
collection_limits.sponsor_transfer_timeout
} else {
- <limit!(T, NftSponsorTransferTimeout)>::get()
+ NFT_SPONSOR_TRANSFER_TIMEOUT
};
let mut sponsor = true;
@@ -75,7 +75,7 @@
let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
collection_limits.sponsor_transfer_timeout
} else {
- <limit!(T, FungibleSponsorTransferTimeout)>::get()
+ FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
};
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
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 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,43 FungibleItemType, ReFungibleItemType,44};4546#[cfg(test)]47mod mock;4849#[cfg(test)]50mod tests;5152mod default_weights;53mod eth;54mod sponsorship;55pub use sponsorship::NftSponsorshipHandler;56pub use eth::sponsoring::NftEthSponsorshipHandler;5758pub use eth::NftErcSupport;59pub use eth::account::*;60use eth::erc::{ERC20Events, ERC721Events};6162#[cfg(feature = "runtime-benchmarks")]63mod benchmarking;6465pub trait WeightInfo {66 fn create_collection() -> Weight;67 fn destroy_collection() -> Weight;68 fn add_to_white_list() -> Weight;69 fn remove_from_white_list() -> Weight;70 fn set_public_access_mode() -> Weight;71 fn set_mint_permission() -> Weight;72 fn change_collection_owner() -> Weight;73 fn add_collection_admin() -> Weight;74 fn remove_collection_admin() -> Weight;75 fn set_collection_sponsor() -> Weight;76 fn confirm_sponsorship() -> Weight;77 fn remove_collection_sponsor() -> Weight;78 fn create_item(s: usize) -> Weight;79 fn burn_item() -> Weight;80 fn transfer() -> Weight;81 fn approve() -> Weight;82 fn transfer_from() -> Weight;83 fn set_offchain_schema() -> Weight;84 fn set_const_on_chain_schema() -> Weight;85 fn set_variable_on_chain_schema() -> Weight;86 fn set_variable_meta_data() -> Weight;87 fn enable_contract_sponsoring() -> Weight;88 fn set_schema_version() -> Weight;89 fn set_chain_limits() -> Weight;90 fn set_contract_sponsoring_rate_limit() -> Weight;91 fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;92 fn toggle_contract_white_list() -> Weight;93 fn add_to_contract_white_list() -> Weight;94 fn remove_from_contract_white_list() -> Weight;95 fn set_collection_limits() -> Weight;96}9798decl_error! {99 /// Error for non-fungible-token module.100 pub enum Error for Module<T: Config> {101 /// Total collections bound exceeded.102 TotalCollectionsLimitExceeded,103 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.104 CollectionDecimalPointLimitExceeded,105 /// Collection name can not be longer than 63 char.106 CollectionNameLimitExceeded,107 /// Collection description can not be longer than 255 char.108 CollectionDescriptionLimitExceeded,109 /// Token prefix can not be longer than 15 char.110 CollectionTokenPrefixLimitExceeded,111 /// This collection does not exist.112 CollectionNotFound,113 /// Item not exists.114 TokenNotFound,115 /// Admin not found116 AdminNotFound,117 /// Arithmetic calculation overflow.118 NumOverflow,119 /// Account already has admin role.120 AlreadyAdmin,121 /// You do not own this collection.122 NoPermission,123 /// This address is not set as sponsor, use setCollectionSponsor first.124 ConfirmUnsetSponsorFail,125 /// Collection is not in mint mode.126 PublicMintingNotAllowed,127 /// Sender parameter and item owner must be equal.128 MustBeTokenOwner,129 /// Item balance not enough.130 TokenValueTooLow,131 /// Size of item is too large.132 NftSizeLimitExceeded,133 /// No approve found134 ApproveNotFound,135 /// Requested value more than approved.136 TokenValueNotEnough,137 /// Only approved addresses can call this method.138 ApproveRequired,139 /// Address is not in white list.140 AddresNotInWhiteList,141 /// Number of collection admins bound exceeded.142 CollectionAdminsLimitExceeded,143 /// Owned tokens by a single address bound exceeded.144 AddressOwnershipLimitExceeded,145 /// Length of items properties must be greater than 0.146 EmptyArgument,147 /// const_data exceeded data limit.148 TokenConstDataLimitExceeded,149 /// variable_data exceeded data limit.150 TokenVariableDataLimitExceeded,151 /// Not NFT item data used to mint in NFT collection.152 NotNftDataUsedToMintNftCollectionToken,153 /// Not Fungible item data used to mint in Fungible collection.154 NotFungibleDataUsedToMintFungibleCollectionToken,155 /// Not Re Fungible item data used to mint in Re Fungible collection.156 NotReFungibleDataUsedToMintReFungibleCollectionToken,157 /// Unexpected collection type.158 UnexpectedCollectionType,159 /// Can't store metadata in fungible tokens.160 CantStoreMetadataInFungibleTokens,161 /// Collection token limit exceeded162 CollectionTokenLimitExceeded,163 /// Account token limit exceeded per collection164 AccountTokenLimitExceeded,165 /// Collection limit bounds per collection exceeded166 CollectionLimitBoundsExceeded,167 /// Tried to enable permissions which are only permitted to be disabled168 OwnerPermissionsCantBeReverted,169 /// Schema data size limit bound exceeded170 SchemaDataLimitExceeded,171 /// Maximum refungibility exceeded172 WrongRefungiblePieces,173 /// createRefungible should be called with one owner174 BadCreateRefungibleCall,175 /// Gas limit exceeded176 OutOfGas,177 /// Collection settings not allowing items transferring178 TransferNotAllowed,179 }180}181182#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]183pub struct CollectionHandle<T: Config> {184 pub id: CollectionId,185 collection: Collection<T>,186 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,187}188impl<T: Config> CollectionHandle<T> {189 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {190 <CollectionById<T>>::get(id).map(|collection| Self {191 id,192 collection,193 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(194 eth::collection_id_to_address(id),195 gas_limit,196 ),197 })198 }199 pub fn get(id: CollectionId) -> Option<Self> {200 Self::get_with_gas_limit(id, u64::MAX)201 }202 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {203 self.recorder.log_sub(log)204 }205 fn consume_gas(&self, gas: u64) -> DispatchResult {206 self.recorder.consume_gas_sub(gas)207 }208 pub fn submit_logs(self) -> DispatchResult {209 self.recorder.submit_logs()210 }211 pub fn save(self) -> DispatchResult {212 self.recorder.submit_logs()?;213 <CollectionById<T>>::insert(self.id, self.collection);214 Ok(())215 }216}217impl<T: Config> Deref for CollectionHandle<T> {218 type Target = Collection<T>;219220 fn deref(&self) -> &Self::Target {221 &self.collection222 }223}224225impl<T: Config> DerefMut for CollectionHandle<T> {226 fn deref_mut(&mut self) -> &mut Self::Target {227 &mut self.collection228 }229}230231pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {232 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;233234 /// Weight information for extrinsics in this pallet.235 type WeightInfo: WeightInfo;236237 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;238 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;239240 type CrossAccountId: CrossAccountId<Self::AccountId>;241 type Currency: Currency<Self::AccountId>;242 type CollectionCreationPrice: Get<243 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,244 >;245 type TreasuryAccountId: Get<Self::AccountId>;246 type ChainLimits: ChainLimits;247}248249pub type ChainLimitsOf<T> = <T as Config>::ChainLimits;250#[macro_export]251macro_rules! limit {252 ($config:ty, $limit:ident) => {253 <$crate::ChainLimitsOf<$config> as nft_data_structs::ChainLimits>::$limit254 }255}256257// # Used definitions258//259// ## User control levels260//261// chain-controlled - key is uncontrolled by user262// i.e autoincrementing index263// can use non-cryptographic hash264// real - key is controlled by user265// but it is hard to generate enough colliding values, i.e owner of signed txs266// can use non-cryptographic hash267// controlled - key is completly controlled by users268// i.e maps with mutable keys269// should use cryptographic hash270//271// ## User control level downgrade reasons272//273// ?1 - chain-controlled -> controlled274// collections/tokens can be destroyed, resulting in massive holes275// ?2 - chain-controlled -> controlled276// same as ?1, but can be only added, resulting in easier exploitation277// ?3 - real -> controlled278// no confirmation required, so addresses can be easily generated279decl_storage! {280 trait Store for Module<T: Config> as Nft {281282 //#region Private members283 /// Id of next collection284 CreatedCollectionCount: u32;285 /// Used for migrations286 ChainVersion: u64;287 /// Id of last collection token288 /// Collection id (controlled?1)289 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;290 //#endregion291292 //#region Bound counters293 /// Amount of collections destroyed, used for total amount tracking with294 /// CreatedCollectionCount295 DestroyedCollectionCount: u32;296 /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)297 /// Account id (real)298 pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;299 //#endregion300301 //#region Basic collections302 /// Collection info303 /// Collection id (controlled?1)304 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;305 /// List of collection admins306 /// Collection id (controlled?2)307 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;308 /// Whitelisted collection users309 /// Collection id (controlled?2), user id (controlled?3)310 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;311 //#endregion312313 /// How many of collection items user have314 /// Collection id (controlled?2), account id (real)315 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;316317 /// Amount of items which spender can transfer out of owners account (via transferFrom)318 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))319 /// TODO: Off chain worker should remove from this map when token gets removed320 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;321322 //#region Item collections323 /// Collection id (controlled?2), token id (controlled?1)324 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;325 /// Collection id (controlled?2), owner (controlled?2)326 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;327 /// Collection id (controlled?2), token id (controlled?1)328 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;329 //#endregion330331 //#region Index list332 /// Collection id (controlled?2), tokens owner (controlled?2)333 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;334 //#endregion335336 //#region Tokens transfer rate limit baskets337 /// (Collection id (controlled?2), who created (real))338 /// TODO: Off chain worker should remove from this map when collection gets removed339 pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;340 /// Collection id (controlled?2), token id (controlled?2)341 pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;342 /// Collection id (controlled?2), owning user (real)343 pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;344 /// Collection id (controlled?2), token id (controlled?2)345 pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;346 //#endregion347348 /// Variable metadata sponsoring349 /// Collection id (controlled?2), token id (controlled?2)350 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;351 }352 add_extra_genesis {353 build(|config: &GenesisConfig<T>| {354 // Modification of storage355 for (_num, _c) in &config.collection_id {356 <Module<T>>::init_collection(_c);357 }358359 for (_num, _c, _i) in &config.nft_item_id {360 <Module<T>>::init_nft_token(*_c, _i);361 }362363 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {364 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);365 }366367 for (_num, _c, _i) in &config.refungible_item_id {368 <Module<T>>::init_refungible_token(*_c, _i);369 }370 })371 }372}373374decl_event!(375 pub enum Event<T>376 where377 AccountId = <T as frame_system::Config>::AccountId,378 CrossAccountId = <T as Config>::CrossAccountId,379 {380 /// New collection was created381 ///382 /// # Arguments383 ///384 /// * collection_id: Globally unique identifier of newly created collection.385 ///386 /// * mode: [CollectionMode] converted into u8.387 ///388 /// * account_id: Collection owner.389 CollectionCreated(CollectionId, u8, AccountId),390391 /// New item was created.392 ///393 /// # Arguments394 ///395 /// * collection_id: Id of the collection where item was created.396 ///397 /// * item_id: Id of an item. Unique within the collection.398 ///399 /// * recipient: Owner of newly created item400 ItemCreated(CollectionId, TokenId, CrossAccountId),401402 /// Collection item was burned.403 ///404 /// # Arguments405 ///406 /// collection_id.407 ///408 /// item_id: Identifier of burned NFT.409 ItemDestroyed(CollectionId, TokenId),410411 /// Item was transferred412 ///413 /// * collection_id: Id of collection to which item is belong414 ///415 /// * item_id: Id of an item416 ///417 /// * sender: Original owner of item418 ///419 /// * recipient: New owner of item420 ///421 /// * amount: Always 1 for NFT422 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),423424 /// * collection_id425 ///426 /// * item_id427 ///428 /// * sender429 ///430 /// * spender431 ///432 /// * amount433 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),434 }435);436437decl_module! {438 pub struct Module<T: Config> for enum Call439 where440 origin: T::Origin441 {442 fn deposit_event() = default;443 type Error = Error<T>;444445 fn on_initialize(_now: T::BlockNumber) -> Weight {446 0447 }448449 /// 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.450 ///451 /// # Permissions452 ///453 /// * Anyone.454 ///455 /// # Arguments456 ///457 /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.458 ///459 /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.460 ///461 /// * token_prefix: UTF-8 string with token prefix.462 ///463 /// * mode: [CollectionMode] collection type and type dependent data.464 // returns collection ID465 #[weight = <T as Config>::WeightInfo::create_collection()]466 #[transactional]467 pub fn create_collection(origin,468 collection_name: Vec<u16>,469 collection_description: Vec<u16>,470 token_prefix: Vec<u8>,471 mode: CollectionMode) -> DispatchResult {472473 // Anyone can create a collection474 let who = ensure_signed(origin)?;475476 // Take a (non-refundable) deposit of collection creation477 let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();478 imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(479 &T::TreasuryAccountId::get(),480 T::CollectionCreationPrice::get(),481 ));482 <T as Config>::Currency::settle(483 &who,484 imbalance,485 WithdrawReasons::TRANSFER,486 ExistenceRequirement::KeepAlive,487 ).map_err(|_| Error::<T>::NoPermission)?;488489 let decimal_points = match mode {490 CollectionMode::Fungible(points) => points,491 _ => 0492 };493494 let created_count = CreatedCollectionCount::get();495 let destroyed_count = DestroyedCollectionCount::get();496497 // bound Total number of collections498 ensure!(created_count - destroyed_count < <limit!(T, CollectionNumberLimit)>::get(), Error::<T>::TotalCollectionsLimitExceeded);499500 // check params501 ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);502 ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);503 ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);504 ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);505506 // Generate next collection ID507 let next_id = created_count508 .checked_add(1)509 .ok_or(Error::<T>::NumOverflow)?;510511 CreatedCollectionCount::put(next_id);512513 let limits = CollectionLimits {514 sponsored_data_size: <limit!(T, CustomDataLimit)>::get(),515 ..Default::default()516 };517518 // Create new collection519 let new_collection = Collection {520 owner: who.clone(),521 name: collection_name,522 mode: mode.clone(),523 mint_mode: false,524 access: AccessMode::Normal,525 description: collection_description,526 decimal_points,527 token_prefix,528 offchain_schema: Vec::new(),529 schema_version: SchemaVersion::ImageURL,530 sponsorship: SponsorshipState::Disabled,531 variable_on_chain_schema: Vec::new(),532 const_on_chain_schema: Vec::new(),533 limits,534 transfers_enabled: true,535 };536537 // Add new collection to map538 <CollectionById<T>>::insert(next_id, new_collection);539540 // call event541 Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.id(), who));542543 Ok(())544 }545546 /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.547 ///548 /// # Permissions549 ///550 /// * Collection Owner.551 ///552 /// # Arguments553 ///554 /// * collection_id: collection to destroy.555 #[weight = <T as Config>::WeightInfo::destroy_collection()]556 #[transactional]557 pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {558559 let sender = ensure_signed(origin)?;560 let collection = Self::get_collection(collection_id)?;561 Self::check_owner_permissions(&collection, &sender)?;562 if !collection.limits.owner_can_destroy {563 fail!(Error::<T>::NoPermission);564 }565566 <AddressTokens<T>>::remove_prefix(collection_id, None);567 <Allowances<T>>::remove_prefix(collection_id, None);568 <Balance<T>>::remove_prefix(collection_id, None);569 <ItemListIndex>::remove(collection_id);570 <AdminList<T>>::remove(collection_id);571 <CollectionById<T>>::remove(collection_id);572 <WhiteList<T>>::remove_prefix(collection_id, None);573574 <NftItemList<T>>::remove_prefix(collection_id, None);575 <FungibleItemList<T>>::remove_prefix(collection_id, None);576 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);577578 <NftTransferBasket<T>>::remove_prefix(collection_id, None);579 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);580 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);581582 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);583584 DestroyedCollectionCount::put(DestroyedCollectionCount::get()585 .checked_add(1)586 .ok_or(Error::<T>::NumOverflow)?);587588 Ok(())589 }590591 /// Add an address to white list.592 ///593 /// # Permissions594 ///595 /// * Collection Owner596 /// * Collection Admin597 ///598 /// # Arguments599 ///600 /// * collection_id.601 ///602 /// * address.603 #[weight = <T as Config>::WeightInfo::add_to_white_list()]604 #[transactional]605 pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{606607 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);608 let collection = Self::get_collection(collection_id)?;609610 Self::toggle_white_list_internal(611 &sender,612 &collection,613 &address,614 true,615 )?;616617 Ok(())618 }619620 /// Remove an address from white list.621 ///622 /// # Permissions623 ///624 /// * Collection Owner625 /// * Collection Admin626 ///627 /// # Arguments628 ///629 /// * collection_id.630 ///631 /// * address.632 #[weight = <T as Config>::WeightInfo::remove_from_white_list()]633 #[transactional]634 pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{635636 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);637 let collection = Self::get_collection(collection_id)?;638639 Self::toggle_white_list_internal(640 &sender,641 &collection,642 &address,643 false,644 )?;645646 Ok(())647 }648649 /// Toggle between normal and white list access for the methods with access for `Anyone`.650 ///651 /// # Permissions652 ///653 /// * Collection Owner.654 ///655 /// # Arguments656 ///657 /// * collection_id.658 ///659 /// * mode: [AccessMode]660 #[weight = <T as Config>::WeightInfo::set_public_access_mode()]661 #[transactional]662 pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult663 {664 let sender = ensure_signed(origin)?;665666 let mut target_collection = Self::get_collection(collection_id)?;667 Self::check_owner_permissions(&target_collection, &sender)?;668 target_collection.access = mode;669 target_collection.save()670 }671672 /// Allows Anyone to create tokens if:673 /// * White List is enabled, and674 /// * Address is added to white list, and675 /// * This method was called with True parameter676 ///677 /// # Permissions678 /// * Collection Owner679 ///680 /// # Arguments681 ///682 /// * collection_id.683 ///684 /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.685 #[weight = <T as Config>::WeightInfo::set_mint_permission()]686 #[transactional]687 pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult688 {689 let sender = ensure_signed(origin)?;690691 let mut target_collection = Self::get_collection(collection_id)?;692 Self::check_owner_permissions(&target_collection, &sender)?;693 target_collection.mint_mode = mint_permission;694 target_collection.save()695 }696697 /// Change the owner of the collection.698 ///699 /// # Permissions700 ///701 /// * Collection Owner.702 ///703 /// # Arguments704 ///705 /// * collection_id.706 ///707 /// * new_owner.708 #[weight = <T as Config>::WeightInfo::change_collection_owner()]709 #[transactional]710 pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {711712 let sender = ensure_signed(origin)?;713 let mut target_collection = Self::get_collection(collection_id)?;714 Self::check_owner_permissions(&target_collection, &sender)?;715 target_collection.owner = new_owner;716 target_collection.save()717 }718719 /// Adds an admin of the Collection.720 /// 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.721 ///722 /// # Permissions723 ///724 /// * Collection Owner.725 /// * Collection Admin.726 ///727 /// # Arguments728 ///729 /// * collection_id: ID of the Collection to add admin for.730 ///731 /// * new_admin_id: Address of new admin to add.732 #[weight = <T as Config>::WeightInfo::add_collection_admin()]733 #[transactional]734 pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {735 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);736 let collection = Self::get_collection(collection_id)?;737 Self::check_owner_or_admin_permissions(&collection, &sender)?;738 let mut admin_arr = <AdminList<T>>::get(collection_id);739740 match admin_arr.binary_search(&new_admin_id) {741 Ok(_) => {},742 Err(idx) => {743 ensure!(admin_arr.len() < <limit!(T, CollectionAdminsLimit)>::get() as usize, Error::<T>::CollectionAdminsLimitExceeded);744 admin_arr.insert(idx, new_admin_id);745 <AdminList<T>>::insert(collection_id, admin_arr);746 }747 }748 Ok(())749 }750751 /// 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.752 ///753 /// # Permissions754 ///755 /// * Collection Owner.756 /// * Collection Admin.757 ///758 /// # Arguments759 ///760 /// * collection_id: ID of the Collection to remove admin for.761 ///762 /// * account_id: Address of admin to remove.763 #[weight = <T as Config>::WeightInfo::remove_collection_admin()]764 #[transactional]765 pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {766 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);767 let collection = Self::get_collection(collection_id)?;768 Self::check_owner_or_admin_permissions(&collection, &sender)?;769 let mut admin_arr = <AdminList<T>>::get(collection_id);770771 if let Ok(idx) = admin_arr.binary_search(&account_id) {772 admin_arr.remove(idx);773 <AdminList<T>>::insert(collection_id, admin_arr);774 }775 Ok(())776 }777778 /// # Permissions779 ///780 /// * Collection Owner781 ///782 /// # Arguments783 ///784 /// * collection_id.785 ///786 /// * new_sponsor.787 #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]788 #[transactional]789 pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {790 let sender = ensure_signed(origin)?;791 let mut target_collection = Self::get_collection(collection_id)?;792 Self::check_owner_permissions(&target_collection, &sender)?;793794 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);795 target_collection.save()796 }797798 /// # Permissions799 ///800 /// * Sponsor.801 ///802 /// # Arguments803 ///804 /// * collection_id.805 #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]806 #[transactional]807 pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {808 let sender = ensure_signed(origin)?;809810 let mut target_collection = Self::get_collection(collection_id)?;811 ensure!(812 target_collection.sponsorship.pending_sponsor() == Some(&sender),813 Error::<T>::ConfirmUnsetSponsorFail814 );815816 target_collection.sponsorship = SponsorshipState::Confirmed(sender);817 target_collection.save()818 }819820 /// Switch back to pay-per-own-transaction model.821 ///822 /// # Permissions823 ///824 /// * Collection owner.825 ///826 /// # Arguments827 ///828 /// * collection_id.829 #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]830 #[transactional]831 pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {832 let sender = ensure_signed(origin)?;833834 let mut target_collection = Self::get_collection(collection_id)?;835 Self::check_owner_permissions(&target_collection, &sender)?;836837 target_collection.sponsorship = SponsorshipState::Disabled;838 target_collection.save()839 }840841 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.842 ///843 /// # Permissions844 ///845 /// * Collection Owner.846 /// * Collection Admin.847 /// * Anyone if848 /// * White List is enabled, and849 /// * Address is added to white list, and850 /// * MintPermission is enabled (see SetMintPermission method)851 ///852 /// # Arguments853 ///854 /// * collection_id: ID of the collection.855 ///856 /// * owner: Address, initial owner of the NFT.857 ///858 /// * data: Token data to store on chain.859 // #[weight =860 // (130_000_000 as Weight)861 // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))862 // .saturating_add(RocksDbWeight::get().reads(10 as Weight))863 // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]864865 #[weight = <T as Config>::WeightInfo::create_item(data.data_size())]866 #[transactional]867 pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData<ChainLimitsOf<T>>) -> DispatchResult {868 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);869 let collection = Self::get_collection(collection_id)?;870871 Self::create_item_internal(&sender, &collection, &owner, data)?;872873 collection.submit_logs()874 }875876 /// This method creates multiple items in a collection created with CreateCollection method.877 ///878 /// # Permissions879 ///880 /// * Collection Owner.881 /// * Collection Admin.882 /// * Anyone if883 /// * White List is enabled, and884 /// * Address is added to white list, and885 /// * MintPermission is enabled (see SetMintPermission method)886 ///887 /// # Arguments888 ///889 /// * collection_id: ID of the collection.890 ///891 /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].892 ///893 /// * owner: Address, initial owner of the NFT.894 #[weight = <T as Config>::WeightInfo::create_item(items_data.iter()895 .map(|data| { data.data_size() })896 .sum())]897 #[transactional]898 pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData<ChainLimitsOf<T>>>) -> DispatchResult {899900 ensure!(!items_data.is_empty(), Error::<T>::EmptyArgument);901 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);902 let collection = Self::get_collection(collection_id)?;903904 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;905906 collection.submit_logs()907 }908909 // TODO! transaction weight910911 /// Set transfers_enabled value for particular collection912 ///913 /// # Permissions914 ///915 /// * Collection Owner.916 ///917 /// # Arguments918 ///919 /// * collection_id: ID of the collection.920 ///921 /// * value: New flag value.922 #[weight = <T as Config>::WeightInfo::burn_item()]923 #[transactional]924 pub fn set_transfers_enabled_flag(origin, collection_id: CollectionId, value: bool) -> DispatchResult {925926 let sender = ensure_signed(origin)?;927 let mut target_collection = Self::get_collection(collection_id)?;928929 Self::check_owner_permissions(&target_collection, &sender)?;930931 target_collection.transfers_enabled = value;932 target_collection.save()933 }934935 /// Destroys a concrete instance of NFT.936 ///937 /// # Permissions938 ///939 /// * Collection Owner.940 /// * Collection Admin.941 /// * Current NFT Owner.942 ///943 /// # Arguments944 ///945 /// * collection_id: ID of the collection.946 ///947 /// * item_id: ID of NFT to burn.948 #[weight = <T as Config>::WeightInfo::burn_item()]949 #[transactional]950 pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {951952 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);953 let target_collection = Self::get_collection(collection_id)?;954955 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;956957 target_collection.submit_logs()958 }959960 /// Change ownership of the token.961 ///962 /// # Permissions963 ///964 /// * Collection Owner965 /// * Collection Admin966 /// * Current NFT owner967 ///968 /// # Arguments969 ///970 /// * recipient: Address of token recipient.971 ///972 /// * collection_id.973 ///974 /// * item_id: ID of the item975 /// * Non-Fungible Mode: Required.976 /// * Fungible Mode: Ignored.977 /// * Re-Fungible Mode: Required.978 ///979 /// * value: Amount to transfer.980 /// * Non-Fungible Mode: Ignored981 /// * Fungible Mode: Must specify transferred amount982 /// * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)983 #[weight = <T as Config>::WeightInfo::transfer()]984 #[transactional]985 pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {986 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);987 let collection = Self::get_collection(collection_id)?;988989 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;990991 collection.submit_logs()992 }993994 /// Set, change, or remove approved address to transfer the ownership of the NFT.995 ///996 /// # Permissions997 ///998 /// * Collection Owner999 /// * Collection Admin1000 /// * Current NFT owner1001 ///1002 /// # Arguments1003 ///1004 /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1005 ///1006 /// * collection_id.1007 ///1008 /// * item_id: ID of the item.1009 #[weight = <T as Config>::WeightInfo::approve()]1010 #[transactional]1011 pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1012 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1013 let collection = Self::get_collection(collection_id)?;10141015 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10161017 collection.submit_logs()1018 }10191020 /// 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.1021 ///1022 /// # Permissions1023 /// * Collection Owner1024 /// * Collection Admin1025 /// * Current NFT owner1026 /// * Address approved by current NFT owner1027 ///1028 /// # Arguments1029 ///1030 /// * from: Address that owns token.1031 ///1032 /// * recipient: Address of token recipient.1033 ///1034 /// * collection_id.1035 ///1036 /// * item_id: ID of the item.1037 ///1038 /// * value: Amount to transfer.1039 #[weight = <T as Config>::WeightInfo::transfer_from()]1040 #[transactional]1041 pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1042 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1043 let collection = Self::get_collection(collection_id)?;10441045 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;10461047 collection.submit_logs()1048 }1049 // #[weight = 0]1050 // // let no_perm_mes = "You do not have permissions to modify this collection";1051 // // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1052 // // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1053 // // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);10541055 // // // on_nft_received call10561057 // // Self::transfer(origin, collection_id, item_id, new_owner)?;10581059 // Ok(())1060 // }10611062 /// Set off-chain data schema.1063 ///1064 /// # Permissions1065 ///1066 /// * Collection Owner1067 /// * Collection Admin1068 ///1069 /// # Arguments1070 ///1071 /// * collection_id.1072 ///1073 /// * schema: String representing the offchain data schema.1074 #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1075 #[transactional]1076 pub fn set_variable_meta_data (1077 origin,1078 collection_id: CollectionId,1079 item_id: TokenId,1080 data: Vec<u8>1081 ) -> DispatchResult {1082 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);10831084 let collection = Self::get_collection(collection_id)?;10851086 Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;10871088 Ok(())1089 }10901091 /// Set schema standard1092 /// ImageURL1093 /// Unique1094 ///1095 /// # Permissions1096 ///1097 /// * Collection Owner1098 /// * Collection Admin1099 ///1100 /// # Arguments1101 ///1102 /// * collection_id.1103 ///1104 /// * schema: SchemaVersion: enum1105 #[weight = <T as Config>::WeightInfo::set_schema_version()]1106 #[transactional]1107 pub fn set_schema_version(1108 origin,1109 collection_id: CollectionId,1110 version: SchemaVersion1111 ) -> DispatchResult {1112 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1113 let mut target_collection = Self::get_collection(collection_id)?;1114 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1115 target_collection.schema_version = version;1116 target_collection.save()1117 }11181119 /// Set off-chain data schema.1120 ///1121 /// # Permissions1122 ///1123 /// * Collection Owner1124 /// * Collection Admin1125 ///1126 /// # Arguments1127 ///1128 /// * collection_id.1129 ///1130 /// * schema: String representing the offchain data schema.1131 #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1132 #[transactional]1133 pub fn set_offchain_schema(1134 origin,1135 collection_id: CollectionId,1136 schema: Vec<u8>1137 ) -> DispatchResult {1138 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1139 let mut target_collection = Self::get_collection(collection_id)?;1140 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11411142 // check schema limit1143 ensure!(schema.len() as u32 <= <limit!(T, OffchainSchemaLimit)>::get(), "");11441145 target_collection.offchain_schema = schema;1146 target_collection.save()1147 }11481149 /// Set const on-chain data schema.1150 ///1151 /// # Permissions1152 ///1153 /// * Collection Owner1154 /// * Collection Admin1155 ///1156 /// # Arguments1157 ///1158 /// * collection_id.1159 ///1160 /// * schema: String representing the const on-chain data schema.1161 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1162 #[transactional]1163 pub fn set_const_on_chain_schema (1164 origin,1165 collection_id: CollectionId,1166 schema: Vec<u8>1167 ) -> DispatchResult {1168 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1169 let mut target_collection = Self::get_collection(collection_id)?;1170 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;11711172 // check schema limit1173 ensure!(schema.len() as u32 <= <limit!(T, ConstOnChainSchemaLimit)>::get(), "");11741175 target_collection.const_on_chain_schema = schema;1176 target_collection.save()1177 }11781179 /// Set variable on-chain data schema.1180 ///1181 /// # Permissions1182 ///1183 /// * Collection Owner1184 /// * Collection Admin1185 ///1186 /// # Arguments1187 ///1188 /// * collection_id.1189 ///1190 /// * schema: String representing the variable on-chain data schema.1191 #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1192 #[transactional]1193 pub fn set_variable_on_chain_schema (1194 origin,1195 collection_id: CollectionId,1196 schema: Vec<u8>1197 ) -> DispatchResult {1198 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1199 let mut target_collection = Self::get_collection(collection_id)?;1200 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;12011202 // check schema limit1203 ensure!(schema.len() as u32 <= <limit!(T, VariableOnChainSchemaLimit)>::get(), "");12041205 target_collection.variable_on_chain_schema = schema;1206 target_collection.save()1207 }12081209 #[weight = <T as Config>::WeightInfo::set_collection_limits()]1210 #[transactional]1211 pub fn set_collection_limits(1212 origin,1213 collection_id: u32,1214 new_limits: CollectionLimits<T::BlockNumber>,1215 ) -> DispatchResult {1216 let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1217 let mut target_collection = Self::get_collection(collection_id)?;1218 Self::check_owner_permissions(&target_collection, sender.as_sub())?;1219 let old_limits = &target_collection.limits;12201221 // collection bounds1222 ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1223 new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP &&1224 new_limits.sponsored_data_size <= <ChainLimitsOf<T> as ChainLimits>::CustomDataLimit::get(),1225 Error::<T>::CollectionLimitBoundsExceeded);12261227 // token_limit check prev1228 ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1229 ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);12301231 ensure!(1232 (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1233 (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1234 Error::<T>::OwnerPermissionsCantBeReverted,1235 );12361237 target_collection.limits = new_limits;12381239 target_collection.save()1240 }1241 }1242}12431244impl<T: Config> Module<T> {1245 pub fn create_item_internal(1246 sender: &T::CrossAccountId,1247 collection: &CollectionHandle<T>,1248 owner: &T::CrossAccountId,1249 data: CreateItemData<ChainLimitsOf<T>>,1250 ) -> DispatchResult {1251 Self::can_create_items_in_collection(collection, sender, owner, 1)?;1252 Self::validate_create_item_args(collection, &data)?;1253 Self::create_item_no_validation(collection, owner, data)?;12541255 Ok(())1256 }12571258 pub fn transfer_internal(1259 sender: &T::CrossAccountId,1260 recipient: &T::CrossAccountId,1261 target_collection: &CollectionHandle<T>,1262 item_id: TokenId,1263 value: u128,1264 ) -> DispatchResult {1265 target_collection.consume_gas(2000000)?;1266 // Limits check1267 Self::is_correct_transfer(target_collection, recipient)?;12681269 // Transfer permissions check1270 ensure!(1271 Self::is_item_owner(sender, target_collection, item_id)1272 || Self::is_owner_or_admin_permissions(target_collection, sender),1273 Error::<T>::NoPermission1274 );12751276 if target_collection.access == AccessMode::WhiteList {1277 Self::check_white_list(target_collection, sender)?;1278 Self::check_white_list(target_collection, recipient)?;1279 }12801281 match target_collection.mode {1282 CollectionMode::NFT => Self::transfer_nft(1283 target_collection,1284 item_id,1285 sender.clone(),1286 recipient.clone(),1287 )?,1288 CollectionMode::Fungible(_) => {1289 Self::transfer_fungible(target_collection, value, sender, recipient)?1290 }1291 CollectionMode::ReFungible => Self::transfer_refungible(1292 target_collection,1293 item_id,1294 value,1295 sender.clone(),1296 recipient.clone(),1297 )?,1298 _ => (),1299 };13001301 Self::deposit_event(RawEvent::Transfer(1302 target_collection.id,1303 item_id,1304 sender.clone(),1305 recipient.clone(),1306 value,1307 ));13081309 Ok(())1310 }13111312 pub fn approve_internal(1313 sender: &T::CrossAccountId,1314 spender: &T::CrossAccountId,1315 collection: &CollectionHandle<T>,1316 item_id: TokenId,1317 amount: u128,1318 ) -> DispatchResult {1319 collection.consume_gas(2000000)?;1320 Self::token_exists(collection, item_id)?;13211322 // Transfer permissions check1323 let bypasses_limits = collection.limits.owner_can_transfer1324 && Self::is_owner_or_admin_permissions(collection, sender);13251326 let allowance_limit = if bypasses_limits {1327 None1328 } else if let Some(amount) = Self::owned_amount(sender, collection, item_id) {1329 Some(amount)1330 } else {1331 fail!(Error::<T>::NoPermission);1332 };13331334 if collection.access == AccessMode::WhiteList {1335 Self::check_white_list(collection, sender)?;1336 Self::check_white_list(collection, spender)?;1337 }13381339 let allowance: u128 = amount1340 .checked_add(<Allowances<T>>::get(1341 collection.id,1342 (item_id, sender.as_sub(), spender.as_sub()),1343 ))1344 .ok_or(Error::<T>::NumOverflow)?;1345 if let Some(limit) = allowance_limit {1346 ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1347 }1348 <Allowances<T>>::insert(1349 collection.id,1350 (item_id, sender.as_sub(), spender.as_sub()),1351 allowance,1352 );13531354 if matches!(collection.mode, CollectionMode::NFT) {1355 // TODO: NFT: only one owner may exist for token in ERC7211356 collection.log(ERC721Events::Approval {1357 owner: *sender.as_eth(),1358 approved: *spender.as_eth(),1359 token_id: item_id.into(),1360 })?;1361 }13621363 if matches!(collection.mode, CollectionMode::Fungible(_)) {1364 // TODO: NFT: only one owner may exist for token in ERC201365 collection.log(ERC20Events::Approval {1366 owner: *sender.as_eth(),1367 spender: *spender.as_eth(),1368 value: allowance.into(),1369 })?;1370 }13711372 Self::deposit_event(RawEvent::Approved(1373 collection.id,1374 item_id,1375 sender.clone(),1376 spender.clone(),1377 allowance,1378 ));1379 Ok(())1380 }13811382 pub fn transfer_from_internal(1383 sender: &T::CrossAccountId,1384 from: &T::CrossAccountId,1385 recipient: &T::CrossAccountId,1386 collection: &CollectionHandle<T>,1387 item_id: TokenId,1388 amount: u128,1389 ) -> DispatchResult {1390 collection.consume_gas(2000000)?;1391 // Check approval1392 let approval: u128 =1393 <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));13941395 // Limits check1396 Self::is_correct_transfer(collection, recipient)?;13971398 // Transfer permissions check1399 ensure!(1400 approval >= amount1401 || (collection.limits.owner_can_transfer1402 && Self::is_owner_or_admin_permissions(collection, sender)),1403 Error::<T>::NoPermission1404 );14051406 if collection.access == AccessMode::WhiteList {1407 Self::check_white_list(collection, sender)?;1408 Self::check_white_list(collection, recipient)?;1409 }14101411 // Reduce approval by transferred amount or remove if remaining approval drops to 01412 let allowance = approval.saturating_sub(amount);1413 if allowance > 0 {1414 <Allowances<T>>::insert(1415 collection.id,1416 (item_id, from.as_sub(), sender.as_sub()),1417 allowance,1418 );1419 } else {1420 <Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1421 }14221423 match collection.mode {1424 CollectionMode::NFT => {1425 Self::transfer_nft(collection, item_id, from.clone(), recipient.clone())?1426 }1427 CollectionMode::Fungible(_) => {1428 Self::transfer_fungible(collection, amount, from, recipient)?1429 }1430 CollectionMode::ReFungible => Self::transfer_refungible(1431 collection,1432 item_id,1433 amount,1434 from.clone(),1435 recipient.clone(),1436 )?,1437 _ => (),1438 };14391440 if matches!(collection.mode, CollectionMode::Fungible(_)) {1441 collection.log(ERC20Events::Approval {1442 owner: *from.as_eth(),1443 spender: *sender.as_eth(),1444 value: allowance.into(),1445 })?;1446 }14471448 Ok(())1449 }14501451 pub fn set_variable_meta_data_internal(1452 sender: &T::CrossAccountId,1453 collection: &CollectionHandle<T>,1454 item_id: TokenId,1455 data: Vec<u8>,1456 ) -> DispatchResult {1457 Self::token_exists(collection, item_id)?;14581459 ensure!(1460 <limit!(T, CustomDataLimit)>::get() >= data.len() as u32,1461 Error::<T>::TokenVariableDataLimitExceeded1462 );14631464 // Modify permissions check1465 ensure!(1466 Self::is_item_owner(sender, collection, item_id)1467 || Self::is_owner_or_admin_permissions(collection, sender),1468 Error::<T>::NoPermission1469 );14701471 match collection.mode {1472 CollectionMode::NFT => Self::set_nft_variable_data(collection, item_id, data)?,1473 CollectionMode::ReFungible => {1474 Self::set_re_fungible_variable_data(collection, item_id, data)?1475 }1476 CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1477 _ => fail!(Error::<T>::UnexpectedCollectionType),1478 };14791480 Ok(())1481 }14821483 pub fn create_multiple_items_internal(1484 sender: &T::CrossAccountId,1485 collection: &CollectionHandle<T>,1486 owner: &T::CrossAccountId,1487 items_data: Vec<CreateItemData<ChainLimitsOf<T>>>,1488 ) -> DispatchResult {1489 Self::can_create_items_in_collection(collection, sender, owner, items_data.len() as u32)?;14901491 for data in &items_data {1492 Self::validate_create_item_args(collection, data)?;1493 }1494 for data in &items_data {1495 Self::create_item_no_validation(collection, owner, data.clone())?;1496 }14971498 Ok(())1499 }15001501 pub fn burn_item_internal(1502 sender: &T::CrossAccountId,1503 collection: &CollectionHandle<T>,1504 item_id: TokenId,1505 value: u128,1506 ) -> DispatchResult {1507 ensure!(1508 Self::is_item_owner(sender, collection, item_id)1509 || (collection.limits.owner_can_transfer1510 && Self::is_owner_or_admin_permissions(collection, sender)),1511 Error::<T>::NoPermission1512 );15131514 if collection.access == AccessMode::WhiteList {1515 Self::check_white_list(collection, sender)?;1516 }15171518 match collection.mode {1519 CollectionMode::NFT => Self::burn_nft_item(collection, item_id)?,1520 CollectionMode::Fungible(_) => Self::burn_fungible_item(sender, collection, value)?,1521 CollectionMode::ReFungible => Self::burn_refungible_item(collection, item_id, sender)?,1522 _ => (),1523 };15241525 Ok(())1526 }15271528 pub fn toggle_white_list_internal(1529 sender: &T::CrossAccountId,1530 collection: &CollectionHandle<T>,1531 address: &T::CrossAccountId,1532 whitelisted: bool,1533 ) -> DispatchResult {1534 Self::check_owner_or_admin_permissions(collection, sender)?;15351536 if whitelisted {1537 <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1538 } else {1539 <WhiteList<T>>::remove(collection.id, address.as_sub());1540 }15411542 Ok(())1543 }15441545 fn is_correct_transfer(1546 collection: &CollectionHandle<T>,1547 recipient: &T::CrossAccountId,1548 ) -> DispatchResult {1549 let collection_id = collection.id;15501551 // check token limit and account token limit1552 let account_items: u32 =1553 <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1554 ensure!(1555 collection.limits.account_token_ownership_limit > account_items,1556 Error::<T>::AccountTokenLimitExceeded1557 );15581559 // preliminary transfer check1560 ensure!(collection.transfers_enabled, Error::<T>::TransferNotAllowed);15611562 Ok(())1563 }15641565 fn can_create_items_in_collection(1566 collection: &CollectionHandle<T>,1567 sender: &T::CrossAccountId,1568 owner: &T::CrossAccountId,1569 amount: u32,1570 ) -> DispatchResult {1571 let collection_id = collection.id;15721573 // check token limit and account token limit1574 let total_items: u32 = ItemListIndex::get(collection_id)1575 .checked_add(amount)1576 .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1577 let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len()1578 as u32)1579 .checked_add(amount)1580 .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1581 ensure!(1582 collection.limits.token_limit >= total_items,1583 Error::<T>::CollectionTokenLimitExceeded1584 );1585 ensure!(1586 collection.limits.account_token_ownership_limit >= account_items,1587 Error::<T>::AccountTokenLimitExceeded1588 );15891590 if !Self::is_owner_or_admin_permissions(collection, sender) {1591 ensure!(collection.mint_mode, Error::<T>::PublicMintingNotAllowed);1592 Self::check_white_list(collection, owner)?;1593 Self::check_white_list(collection, sender)?;1594 }15951596 Ok(())1597 }15981599 fn validate_create_item_args(1600 target_collection: &CollectionHandle<T>,1601 data: &CreateItemData<ChainLimitsOf<T>>,1602 ) -> DispatchResult {1603 match target_collection.mode {1604 CollectionMode::NFT => {1605 if let CreateItemData::NFT(data) = data {1606 // check sizes1607 ensure!(1608 <limit!(T, CustomDataLimit)>::get() >= data.const_data.len() as u32,1609 Error::<T>::TokenConstDataLimitExceeded1610 );1611 ensure!(1612 <limit!(T, CustomDataLimit)>::get() >= data.variable_data.len() as u32,1613 Error::<T>::TokenVariableDataLimitExceeded1614 );1615 } else {1616 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1617 }1618 }1619 CollectionMode::Fungible(_) => {1620 if let CreateItemData::Fungible(_) = data {1621 } else {1622 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1623 }1624 }1625 CollectionMode::ReFungible => {1626 if let CreateItemData::ReFungible(data) = data {1627 // check sizes1628 ensure!(1629 <limit!(T, CustomDataLimit)>::get() >= data.const_data.len() as u32,1630 Error::<T>::TokenConstDataLimitExceeded1631 );1632 ensure!(1633 <limit!(T, CustomDataLimit)>::get() >= data.variable_data.len() as u32,1634 Error::<T>::TokenVariableDataLimitExceeded1635 );16361637 // Check refungibility limits1638 ensure!(1639 data.pieces <= MAX_REFUNGIBLE_PIECES,1640 Error::<T>::WrongRefungiblePieces1641 );1642 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1643 } else {1644 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1645 }1646 }1647 _ => {1648 fail!(Error::<T>::UnexpectedCollectionType);1649 }1650 };16511652 Ok(())1653 }16541655 fn create_item_no_validation(1656 collection: &CollectionHandle<T>,1657 owner: &T::CrossAccountId,1658 data: CreateItemData<ChainLimitsOf<T>>,1659 ) -> DispatchResult {1660 match data {1661 CreateItemData::NFT(data) => {1662 let item = NftItemType {1663 owner: owner.clone(),1664 const_data: data.const_data.into_inner(),1665 variable_data: data.variable_data.into_inner(),1666 };16671668 Self::add_nft_item(collection, item)?;1669 }1670 CreateItemData::Fungible(data) => {1671 Self::add_fungible_item(collection, owner, data.value)?;1672 }1673 CreateItemData::ReFungible(data) => {1674 let owner_list = vec![Ownership {1675 owner: owner.clone(),1676 fraction: data.pieces,1677 }];16781679 let item = ReFungibleItemType {1680 owner: owner_list,1681 const_data: data.const_data.into_inner(),1682 variable_data: data.variable_data.into_inner(),1683 };16841685 Self::add_refungible_item(collection, item)?;1686 }1687 };16881689 Ok(())1690 }16911692 fn add_fungible_item(1693 collection: &CollectionHandle<T>,1694 owner: &T::CrossAccountId,1695 value: u128,1696 ) -> DispatchResult {1697 let collection_id = collection.id;16981699 // Does new owner already have an account?1700 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;17011702 // Mint1703 let item = FungibleItemType {1704 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1705 };1706 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17071708 // Update balance1709 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1710 .checked_add(value)1711 .ok_or(Error::<T>::NumOverflow)?;1712 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17131714 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1715 Ok(())1716 }17171718 fn add_refungible_item(1719 collection: &CollectionHandle<T>,1720 item: ReFungibleItemType<T::CrossAccountId>,1721 ) -> DispatchResult {1722 let collection_id = collection.id;17231724 let current_index = <ItemListIndex>::get(collection_id)1725 .checked_add(1)1726 .ok_or(Error::<T>::NumOverflow)?;1727 let itemcopy = item.clone();17281729 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1730 let item_owner = item.owner.first().expect("only one owner is defined");17311732 let value = item_owner.fraction;1733 let owner = item_owner.owner.clone();17341735 Self::add_token_index(collection_id, current_index, &owner)?;17361737 <ItemListIndex>::insert(collection_id, current_index);1738 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17391740 // Update balance1741 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1742 .checked_add(value)1743 .ok_or(Error::<T>::NumOverflow)?;1744 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17451746 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1747 Ok(())1748 }17491750 fn add_nft_item(1751 collection: &CollectionHandle<T>,1752 item: NftItemType<T::CrossAccountId>,1753 ) -> DispatchResult {1754 let collection_id = collection.id;17551756 let current_index = <ItemListIndex>::get(collection_id)1757 .checked_add(1)1758 .ok_or(Error::<T>::NumOverflow)?;17591760 let item_owner = item.owner.clone();1761 Self::add_token_index(collection_id, current_index, &item.owner)?;17621763 <ItemListIndex>::insert(collection_id, current_index);1764 <NftItemList<T>>::insert(collection_id, current_index, item);17651766 // Update balance1767 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1768 .checked_add(1)1769 .ok_or(Error::<T>::NumOverflow)?;1770 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17711772 collection.log(ERC721Events::Transfer {1773 from: H160::default(),1774 to: *item_owner.as_eth(),1775 token_id: current_index.into(),1776 })?;1777 Self::deposit_event(RawEvent::ItemCreated(1778 collection_id,1779 current_index,1780 item_owner,1781 ));1782 Ok(())1783 }17841785 fn burn_refungible_item(1786 collection: &CollectionHandle<T>,1787 item_id: TokenId,1788 owner: &T::CrossAccountId,1789 ) -> DispatchResult {1790 let collection_id = collection.id;17911792 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1793 .ok_or(Error::<T>::TokenNotFound)?;1794 let rft_balance = token1795 .owner1796 .iter()1797 .find(|&i| i.owner == *owner)1798 .ok_or(Error::<T>::TokenNotFound)?;1799 Self::remove_token_index(collection_id, item_id, owner)?;18001801 // update balance1802 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1803 .checked_sub(rft_balance.fraction)1804 .ok_or(Error::<T>::NumOverflow)?;1805 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);18061807 // Re-create owners list with sender removed1808 let index = token1809 .owner1810 .iter()1811 .position(|i| i.owner == *owner)1812 .expect("owned item is exists");1813 token.owner.remove(index);1814 let owner_count = token.owner.len();18151816 // Burn the token completely if this was the last (only) owner1817 if owner_count == 0 {1818 <ReFungibleItemList<T>>::remove(collection_id, item_id);1819 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1820 } else {1821 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1822 }18231824 Ok(())1825 }18261827 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1828 let collection_id = collection.id;18291830 let item =1831 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1832 Self::remove_token_index(collection_id, item_id, &item.owner)?;18331834 // update balance1835 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1836 .checked_sub(1)1837 .ok_or(Error::<T>::NumOverflow)?;1838 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1839 <NftItemList<T>>::remove(collection_id, item_id);1840 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18411842 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1843 Ok(())1844 }18451846 fn burn_fungible_item(1847 owner: &T::CrossAccountId,1848 collection: &CollectionHandle<T>,1849 value: u128,1850 ) -> DispatchResult {1851 let collection_id = collection.id;18521853 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1854 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18551856 // update balance1857 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1858 .checked_sub(value)1859 .ok_or(Error::<T>::NumOverflow)?;1860 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18611862 if balance.value - value > 0 {1863 balance.value -= value;1864 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1865 } else {1866 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1867 }18681869 collection.log(ERC20Events::Transfer {1870 from: *owner.as_eth(),1871 to: H160::default(),1872 value: value.into(),1873 })?;1874 Ok(())1875 }18761877 pub fn get_collection(1878 collection_id: CollectionId,1879 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1880 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1881 }18821883 fn check_owner_permissions(1884 target_collection: &CollectionHandle<T>,1885 subject: &T::AccountId,1886 ) -> DispatchResult {1887 ensure!(1888 *subject == target_collection.owner,1889 Error::<T>::NoPermission1890 );18911892 Ok(())1893 }18941895 fn is_owner_or_admin_permissions(1896 collection: &CollectionHandle<T>,1897 subject: &T::CrossAccountId,1898 ) -> bool {1899 *subject.as_sub() == collection.owner1900 || <AdminList<T>>::get(collection.id).contains(subject)1901 }19021903 fn check_owner_or_admin_permissions(1904 collection: &CollectionHandle<T>,1905 subject: &T::CrossAccountId,1906 ) -> DispatchResult {1907 ensure!(1908 Self::is_owner_or_admin_permissions(collection, subject),1909 Error::<T>::NoPermission1910 );19111912 Ok(())1913 }19141915 fn owned_amount(1916 subject: &T::CrossAccountId,1917 target_collection: &CollectionHandle<T>,1918 item_id: TokenId,1919 ) -> Option<u128> {1920 let collection_id = target_collection.id;19211922 match target_collection.mode {1923 CollectionMode::NFT => {1924 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1925 }1926 CollectionMode::Fungible(_) => {1927 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1928 }1929 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1930 .owner1931 .iter()1932 .find(|i| i.owner == *subject)1933 .map(|i| i.fraction),1934 CollectionMode::Invalid => None,1935 }1936 }19371938 fn is_item_owner(1939 subject: &T::CrossAccountId,1940 target_collection: &CollectionHandle<T>,1941 item_id: TokenId,1942 ) -> bool {1943 match target_collection.mode {1944 CollectionMode::Fungible(_) => true,1945 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1946 }1947 }19481949 fn check_white_list(1950 collection: &CollectionHandle<T>,1951 address: &T::CrossAccountId,1952 ) -> DispatchResult {1953 let collection_id = collection.id;19541955 let mes = Error::<T>::AddresNotInWhiteList;1956 ensure!(1957 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1958 mes1959 );19601961 Ok(())1962 }19631964 /// Check if token exists. In case of Fungible, check if there is an entry for1965 /// the owner in fungible balances double map1966 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1967 let collection_id = target_collection.id;1968 let exists = match target_collection.mode {1969 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1970 CollectionMode::Fungible(_) => true,1971 CollectionMode::ReFungible => {1972 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1973 }1974 _ => false,1975 };19761977 ensure!(exists, Error::<T>::TokenNotFound);1978 Ok(())1979 }19801981 fn transfer_fungible(1982 collection: &CollectionHandle<T>,1983 value: u128,1984 owner: &T::CrossAccountId,1985 recipient: &T::CrossAccountId,1986 ) -> DispatchResult {1987 let collection_id = collection.id;19881989 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1990 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19911992 // Send balance to recipient (updates balanceOf of recipient)1993 Self::add_fungible_item(collection, recipient, value)?;19941995 // update balanceOf of sender1996 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19971998 // Reduce or remove sender1999 if balance.value == value {2000 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2001 } else {2002 balance.value -= value;2003 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2004 }20052006 collection.log(ERC20Events::Transfer {2007 from: *owner.as_eth(),2008 to: *recipient.as_eth(),2009 value: value.into(),2010 })?;2011 Self::deposit_event(RawEvent::Transfer(2012 collection.id,2013 1,2014 owner.clone(),2015 recipient.clone(),2016 value,2017 ));20182019 Ok(())2020 }20212022 fn transfer_refungible(2023 collection: &CollectionHandle<T>,2024 item_id: TokenId,2025 value: u128,2026 owner: T::CrossAccountId,2027 new_owner: T::CrossAccountId,2028 ) -> DispatchResult {2029 let collection_id = collection.id;2030 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2031 .ok_or(Error::<T>::TokenNotFound)?;20322033 let item = full_item2034 .owner2035 .iter()2036 .find(|i| i.owner == owner)2037 .ok_or(Error::<T>::TokenNotFound)?;2038 let amount = item.fraction;20392040 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20412042 // update balance2043 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2044 .checked_sub(value)2045 .ok_or(Error::<T>::NumOverflow)?;2046 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20472048 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2049 .checked_add(value)2050 .ok_or(Error::<T>::NumOverflow)?;2051 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20522053 let old_owner = item.owner.clone();2054 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20552056 let mut new_full_item = full_item.clone();2057 // transfer2058 if amount == value && !new_owner_has_account {2059 // change owner2060 // new owner do not have account2061 new_full_item2062 .owner2063 .iter_mut()2064 .find(|i| i.owner == owner)2065 .expect("old owner does present in refungible")2066 .owner = new_owner.clone();2067 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20682069 // update index collection2070 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2071 } else {2072 new_full_item2073 .owner2074 .iter_mut()2075 .find(|i| i.owner == owner)2076 .expect("old owner does present in refungible")2077 .fraction -= value;20782079 // separate amount2080 if new_owner_has_account {2081 // new owner has account2082 new_full_item2083 .owner2084 .iter_mut()2085 .find(|i| i.owner == new_owner)2086 .expect("new owner has account")2087 .fraction += value;2088 } else {2089 // new owner do not have account2090 new_full_item.owner.push(Ownership {2091 owner: new_owner.clone(),2092 fraction: value,2093 });2094 Self::add_token_index(collection_id, item_id, &new_owner)?;2095 }20962097 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2098 }20992100 Self::deposit_event(RawEvent::Transfer(2101 collection.id,2102 item_id,2103 owner,2104 new_owner,2105 amount,2106 ));21072108 Ok(())2109 }21102111 fn transfer_nft(2112 collection: &CollectionHandle<T>,2113 item_id: TokenId,2114 sender: T::CrossAccountId,2115 new_owner: T::CrossAccountId,2116 ) -> DispatchResult {2117 let collection_id = collection.id;2118 let mut item =2119 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21202121 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21222123 // update balance2124 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2125 .checked_sub(1)2126 .ok_or(Error::<T>::NumOverflow)?;2127 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21282129 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2130 .checked_add(1)2131 .ok_or(Error::<T>::NumOverflow)?;2132 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21332134 // change owner2135 let old_owner = item.owner.clone();2136 item.owner = new_owner.clone();2137 <NftItemList<T>>::insert(collection_id, item_id, item);21382139 // update index collection2140 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21412142 collection.log(ERC721Events::Transfer {2143 from: *sender.as_eth(),2144 to: *new_owner.as_eth(),2145 token_id: item_id.into(),2146 })?;2147 Self::deposit_event(RawEvent::Transfer(2148 collection.id,2149 item_id,2150 sender,2151 new_owner,2152 1,2153 ));21542155 Ok(())2156 }21572158 fn set_re_fungible_variable_data(2159 collection: &CollectionHandle<T>,2160 item_id: TokenId,2161 data: Vec<u8>,2162 ) -> DispatchResult {2163 let collection_id = collection.id;2164 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2165 .ok_or(Error::<T>::TokenNotFound)?;21662167 item.variable_data = data;21682169 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21702171 Ok(())2172 }21732174 fn set_nft_variable_data(2175 collection: &CollectionHandle<T>,2176 item_id: TokenId,2177 data: Vec<u8>,2178 ) -> DispatchResult {2179 let collection_id = collection.id;2180 let mut item =2181 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21822183 item.variable_data = data;21842185 <NftItemList<T>>::insert(collection_id, item_id, item);21862187 Ok(())2188 }21892190 #[allow(dead_code)]2191 fn init_collection(item: &Collection<T>) {2192 // check params2193 assert!(2194 item.decimal_points <= MAX_DECIMAL_POINTS,2195 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2196 );2197 assert!(2198 item.name.len() <= 64,2199 "Collection name can not be longer than 63 char"2200 );2201 assert!(2202 item.name.len() <= 256,2203 "Collection description can not be longer than 255 char"2204 );2205 assert!(2206 item.token_prefix.len() <= 16,2207 "Token prefix can not be longer than 15 char"2208 );22092210 // Generate next collection ID2211 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22122213 CreatedCollectionCount::put(next_id);2214 }22152216 #[allow(dead_code)]2217 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2218 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22192220 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22212222 <ItemListIndex>::insert(collection_id, current_index);22232224 // Update balance2225 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2226 .checked_add(1)2227 .unwrap();2228 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2229 }22302231 #[allow(dead_code)]2232 fn init_fungible_token(2233 collection_id: CollectionId,2234 owner: &T::CrossAccountId,2235 item: &FungibleItemType,2236 ) {2237 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22382239 Self::add_token_index(collection_id, current_index, owner).unwrap();22402241 <ItemListIndex>::insert(collection_id, current_index);22422243 // Update balance2244 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2245 .checked_add(item.value)2246 .unwrap();2247 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2248 }22492250 #[allow(dead_code)]2251 fn init_refungible_token(2252 collection_id: CollectionId,2253 item: &ReFungibleItemType<T::CrossAccountId>,2254 ) {2255 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22562257 let value = item.owner.first().unwrap().fraction;2258 let owner = item.owner.first().unwrap().owner.clone();22592260 Self::add_token_index(collection_id, current_index, &owner).unwrap();22612262 <ItemListIndex>::insert(collection_id, current_index);22632264 // Update balance2265 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2266 .checked_add(value)2267 .unwrap();2268 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2269 }22702271 fn add_token_index(2272 collection_id: CollectionId,2273 item_index: TokenId,2274 owner: &T::CrossAccountId,2275 ) -> DispatchResult {2276 // add to account limit2277 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2278 // bound Owned tokens by a single address2279 let count = <AccountItemCount<T>>::get(owner.as_sub());2280 ensure!(2281 count < <limit!(T, AccountTokenOwnershipLimit)>::get(),2282 Error::<T>::AddressOwnershipLimitExceeded2283 );22842285 <AccountItemCount<T>>::insert(2286 owner.as_sub(),2287 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2288 );2289 } else {2290 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2291 }22922293 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2294 if list_exists {2295 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2296 let item_contains = list.contains(&item_index.clone());22972298 if !item_contains {2299 list.push(item_index);2300 }23012302 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2303 } else {2304 let itm = vec![item_index];2305 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2306 }23072308 Ok(())2309 }23102311 fn remove_token_index(2312 collection_id: CollectionId,2313 item_index: TokenId,2314 owner: &T::CrossAccountId,2315 ) -> DispatchResult {2316 // update counter2317 <AccountItemCount<T>>::insert(2318 owner.as_sub(),2319 <AccountItemCount<T>>::get(owner.as_sub())2320 .checked_sub(1)2321 .ok_or(Error::<T>::NumOverflow)?,2322 );23232324 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2325 if list_exists {2326 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2327 let item_contains = list.contains(&item_index.clone());23282329 if item_contains {2330 list.retain(|&item| item != item_index);2331 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2332 }2333 }23342335 Ok(())2336 }23372338 fn move_token_index(2339 collection_id: CollectionId,2340 item_index: TokenId,2341 old_owner: &T::CrossAccountId,2342 new_owner: &T::CrossAccountId,2343 ) -> DispatchResult {2344 Self::remove_token_index(collection_id, item_index, old_owner)?;2345 Self::add_token_index(collection_id, item_index, new_owner)?;23462347 Ok(())2348 }2349}23502351sp_api::decl_runtime_apis! {2352 pub trait NftApi {2353 /// Used for ethereum integration2354 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2355 }2356}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_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 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 let CreateItemData::NFT(data) = data {1599 // check sizes1600 ensure!(1601 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1602 Error::<T>::TokenConstDataLimitExceeded1603 );1604 ensure!(1605 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1606 Error::<T>::TokenVariableDataLimitExceeded1607 );1608 } else {1609 fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1610 }1611 }1612 CollectionMode::Fungible(_) => {1613 if let CreateItemData::Fungible(_) = data {1614 } else {1615 fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1616 }1617 }1618 CollectionMode::ReFungible => {1619 if let CreateItemData::ReFungible(data) = data {1620 // check sizes1621 ensure!(1622 CUSTOM_DATA_LIMIT >= data.const_data.len() as u32,1623 Error::<T>::TokenConstDataLimitExceeded1624 );1625 ensure!(1626 CUSTOM_DATA_LIMIT >= data.variable_data.len() as u32,1627 Error::<T>::TokenVariableDataLimitExceeded1628 );16291630 // Check refungibility limits1631 ensure!(1632 data.pieces <= MAX_REFUNGIBLE_PIECES,1633 Error::<T>::WrongRefungiblePieces1634 );1635 ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1636 } else {1637 fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1638 }1639 }1640 _ => {1641 fail!(Error::<T>::UnexpectedCollectionType);1642 }1643 };16441645 Ok(())1646 }16471648 fn create_item_no_validation(1649 collection: &CollectionHandle<T>,1650 owner: &T::CrossAccountId,1651 data: CreateItemData,1652 ) -> DispatchResult {1653 match data {1654 CreateItemData::NFT(data) => {1655 let item = NftItemType {1656 owner: owner.clone(),1657 const_data: data.const_data.into_inner(),1658 variable_data: data.variable_data.into_inner(),1659 };16601661 Self::add_nft_item(collection, item)?;1662 }1663 CreateItemData::Fungible(data) => {1664 Self::add_fungible_item(collection, owner, data.value)?;1665 }1666 CreateItemData::ReFungible(data) => {1667 let owner_list = vec![Ownership {1668 owner: owner.clone(),1669 fraction: data.pieces,1670 }];16711672 let item = ReFungibleItemType {1673 owner: owner_list,1674 const_data: data.const_data.into_inner(),1675 variable_data: data.variable_data.into_inner(),1676 };16771678 Self::add_refungible_item(collection, item)?;1679 }1680 };16811682 Ok(())1683 }16841685 fn add_fungible_item(1686 collection: &CollectionHandle<T>,1687 owner: &T::CrossAccountId,1688 value: u128,1689 ) -> DispatchResult {1690 let collection_id = collection.id;16911692 // Does new owner already have an account?1693 let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;16941695 // Mint1696 let item = FungibleItemType {1697 value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,1698 };1699 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);17001701 // Update balance1702 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1703 .checked_add(value)1704 .ok_or(Error::<T>::NumOverflow)?;1705 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17061707 Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1708 Ok(())1709 }17101711 fn add_refungible_item(1712 collection: &CollectionHandle<T>,1713 item: ReFungibleItemType<T::CrossAccountId>,1714 ) -> DispatchResult {1715 let collection_id = collection.id;17161717 let current_index = <ItemListIndex>::get(collection_id)1718 .checked_add(1)1719 .ok_or(Error::<T>::NumOverflow)?;1720 let itemcopy = item.clone();17211722 ensure!(item.owner.len() == 1, Error::<T>::BadCreateRefungibleCall,);1723 let item_owner = item.owner.first().expect("only one owner is defined");17241725 let value = item_owner.fraction;1726 let owner = item_owner.owner.clone();17271728 Self::add_token_index(collection_id, current_index, &owner)?;17291730 <ItemListIndex>::insert(collection_id, current_index);1731 <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17321733 // Update balance1734 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1735 .checked_add(value)1736 .ok_or(Error::<T>::NumOverflow)?;1737 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);17381739 Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1740 Ok(())1741 }17421743 fn add_nft_item(1744 collection: &CollectionHandle<T>,1745 item: NftItemType<T::CrossAccountId>,1746 ) -> DispatchResult {1747 let collection_id = collection.id;17481749 let current_index = <ItemListIndex>::get(collection_id)1750 .checked_add(1)1751 .ok_or(Error::<T>::NumOverflow)?;17521753 let item_owner = item.owner.clone();1754 Self::add_token_index(collection_id, current_index, &item.owner)?;17551756 <ItemListIndex>::insert(collection_id, current_index);1757 <NftItemList<T>>::insert(collection_id, current_index, item);17581759 // Update balance1760 let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())1761 .checked_add(1)1762 .ok_or(Error::<T>::NumOverflow)?;1763 <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);17641765 collection.log(ERC721Events::Transfer {1766 from: H160::default(),1767 to: *item_owner.as_eth(),1768 token_id: current_index.into(),1769 })?;1770 Self::deposit_event(RawEvent::ItemCreated(1771 collection_id,1772 current_index,1773 item_owner,1774 ));1775 Ok(())1776 }17771778 fn burn_refungible_item(1779 collection: &CollectionHandle<T>,1780 item_id: TokenId,1781 owner: &T::CrossAccountId,1782 ) -> DispatchResult {1783 let collection_id = collection.id;17841785 let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)1786 .ok_or(Error::<T>::TokenNotFound)?;1787 let rft_balance = token1788 .owner1789 .iter()1790 .find(|&i| i.owner == *owner)1791 .ok_or(Error::<T>::TokenNotFound)?;1792 Self::remove_token_index(collection_id, item_id, owner)?;17931794 // update balance1795 let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())1796 .checked_sub(rft_balance.fraction)1797 .ok_or(Error::<T>::NumOverflow)?;1798 <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);17991800 // Re-create owners list with sender removed1801 let index = token1802 .owner1803 .iter()1804 .position(|i| i.owner == *owner)1805 .expect("owned item is exists");1806 token.owner.remove(index);1807 let owner_count = token.owner.len();18081809 // Burn the token completely if this was the last (only) owner1810 if owner_count == 0 {1811 <ReFungibleItemList<T>>::remove(collection_id, item_id);1812 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1813 } else {1814 <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1815 }18161817 Ok(())1818 }18191820 fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1821 let collection_id = collection.id;18221823 let item =1824 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;1825 Self::remove_token_index(collection_id, item_id, &item.owner)?;18261827 // update balance1828 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())1829 .checked_sub(1)1830 .ok_or(Error::<T>::NumOverflow)?;1831 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);1832 <NftItemList<T>>::remove(collection_id, item_id);1833 <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18341835 Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));1836 Ok(())1837 }18381839 fn burn_fungible_item(1840 owner: &T::CrossAccountId,1841 collection: &CollectionHandle<T>,1842 value: u128,1843 ) -> DispatchResult {1844 let collection_id = collection.id;18451846 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1847 ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18481849 // update balance1850 let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())1851 .checked_sub(value)1852 .ok_or(Error::<T>::NumOverflow)?;1853 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);18541855 if balance.value - value > 0 {1856 balance.value -= value;1857 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1858 } else {1859 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1860 }18611862 collection.log(ERC20Events::Transfer {1863 from: *owner.as_eth(),1864 to: H160::default(),1865 value: value.into(),1866 })?;1867 Ok(())1868 }18691870 pub fn get_collection(1871 collection_id: CollectionId,1872 ) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {1873 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1874 }18751876 fn check_owner_permissions(1877 target_collection: &CollectionHandle<T>,1878 subject: &T::AccountId,1879 ) -> DispatchResult {1880 ensure!(1881 *subject == target_collection.owner,1882 Error::<T>::NoPermission1883 );18841885 Ok(())1886 }18871888 fn is_owner_or_admin_permissions(1889 collection: &CollectionHandle<T>,1890 subject: &T::CrossAccountId,1891 ) -> bool {1892 *subject.as_sub() == collection.owner1893 || <AdminList<T>>::get(collection.id).contains(subject)1894 }18951896 fn check_owner_or_admin_permissions(1897 collection: &CollectionHandle<T>,1898 subject: &T::CrossAccountId,1899 ) -> DispatchResult {1900 ensure!(1901 Self::is_owner_or_admin_permissions(collection, subject),1902 Error::<T>::NoPermission1903 );19041905 Ok(())1906 }19071908 fn owned_amount(1909 subject: &T::CrossAccountId,1910 target_collection: &CollectionHandle<T>,1911 item_id: TokenId,1912 ) -> Option<u128> {1913 let collection_id = target_collection.id;19141915 match target_collection.mode {1916 CollectionMode::NFT => {1917 (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject).then(|| 1)1918 }1919 CollectionMode::Fungible(_) => {1920 Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub()).value)1921 }1922 CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?1923 .owner1924 .iter()1925 .find(|i| i.owner == *subject)1926 .map(|i| i.fraction),1927 CollectionMode::Invalid => None,1928 }1929 }19301931 fn is_item_owner(1932 subject: &T::CrossAccountId,1933 target_collection: &CollectionHandle<T>,1934 item_id: TokenId,1935 ) -> bool {1936 match target_collection.mode {1937 CollectionMode::Fungible(_) => true,1938 _ => Self::owned_amount(subject, target_collection, item_id).is_some(),1939 }1940 }19411942 fn check_white_list(1943 collection: &CollectionHandle<T>,1944 address: &T::CrossAccountId,1945 ) -> DispatchResult {1946 let collection_id = collection.id;19471948 let mes = Error::<T>::AddresNotInWhiteList;1949 ensure!(1950 <WhiteList<T>>::contains_key(collection_id, address.as_sub()),1951 mes1952 );19531954 Ok(())1955 }19561957 /// Check if token exists. In case of Fungible, check if there is an entry for1958 /// the owner in fungible balances double map1959 fn token_exists(target_collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {1960 let collection_id = target_collection.id;1961 let exists = match target_collection.mode {1962 CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),1963 CollectionMode::Fungible(_) => true,1964 CollectionMode::ReFungible => {1965 <ReFungibleItemList<T>>::contains_key(collection_id, item_id)1966 }1967 _ => false,1968 };19691970 ensure!(exists, Error::<T>::TokenNotFound);1971 Ok(())1972 }19731974 fn transfer_fungible(1975 collection: &CollectionHandle<T>,1976 value: u128,1977 owner: &T::CrossAccountId,1978 recipient: &T::CrossAccountId,1979 ) -> DispatchResult {1980 let collection_id = collection.id;19811982 let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());1983 ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);19841985 // Send balance to recipient (updates balanceOf of recipient)1986 Self::add_fungible_item(collection, recipient, value)?;19871988 // update balanceOf of sender1989 <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);19901991 // Reduce or remove sender1992 if balance.value == value {1993 <FungibleItemList<T>>::remove(collection_id, owner.as_sub());1994 } else {1995 balance.value -= value;1996 <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);1997 }19981999 collection.log(ERC20Events::Transfer {2000 from: *owner.as_eth(),2001 to: *recipient.as_eth(),2002 value: value.into(),2003 })?;2004 Self::deposit_event(RawEvent::Transfer(2005 collection.id,2006 1,2007 owner.clone(),2008 recipient.clone(),2009 value,2010 ));20112012 Ok(())2013 }20142015 fn transfer_refungible(2016 collection: &CollectionHandle<T>,2017 item_id: TokenId,2018 value: u128,2019 owner: T::CrossAccountId,2020 new_owner: T::CrossAccountId,2021 ) -> DispatchResult {2022 let collection_id = collection.id;2023 let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2024 .ok_or(Error::<T>::TokenNotFound)?;20252026 let item = full_item2027 .owner2028 .iter()2029 .find(|i| i.owner == owner)2030 .ok_or(Error::<T>::TokenNotFound)?;2031 let amount = item.fraction;20322033 ensure!(amount >= value, Error::<T>::TokenValueTooLow);20342035 // update balance2036 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2037 .checked_sub(value)2038 .ok_or(Error::<T>::NumOverflow)?;2039 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);20402041 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2042 .checked_add(value)2043 .ok_or(Error::<T>::NumOverflow)?;2044 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);20452046 let old_owner = item.owner.clone();2047 let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20482049 let mut new_full_item = full_item.clone();2050 // transfer2051 if amount == value && !new_owner_has_account {2052 // change owner2053 // new owner do not have account2054 new_full_item2055 .owner2056 .iter_mut()2057 .find(|i| i.owner == owner)2058 .expect("old owner does present in refungible")2059 .owner = new_owner.clone();2060 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20612062 // update index collection2063 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2064 } else {2065 new_full_item2066 .owner2067 .iter_mut()2068 .find(|i| i.owner == owner)2069 .expect("old owner does present in refungible")2070 .fraction -= value;20712072 // separate amount2073 if new_owner_has_account {2074 // new owner has account2075 new_full_item2076 .owner2077 .iter_mut()2078 .find(|i| i.owner == new_owner)2079 .expect("new owner has account")2080 .fraction += value;2081 } else {2082 // new owner do not have account2083 new_full_item.owner.push(Ownership {2084 owner: new_owner.clone(),2085 fraction: value,2086 });2087 Self::add_token_index(collection_id, item_id, &new_owner)?;2088 }20892090 <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2091 }20922093 Self::deposit_event(RawEvent::Transfer(2094 collection.id,2095 item_id,2096 owner,2097 new_owner,2098 amount,2099 ));21002101 Ok(())2102 }21032104 fn transfer_nft(2105 collection: &CollectionHandle<T>,2106 item_id: TokenId,2107 sender: T::CrossAccountId,2108 new_owner: T::CrossAccountId,2109 ) -> DispatchResult {2110 let collection_id = collection.id;2111 let mut item =2112 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21132114 ensure!(sender == item.owner, Error::<T>::MustBeTokenOwner);21152116 // update balance2117 let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2118 .checked_sub(1)2119 .ok_or(Error::<T>::NumOverflow)?;2120 <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);21212122 let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2123 .checked_add(1)2124 .ok_or(Error::<T>::NumOverflow)?;2125 <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);21262127 // change owner2128 let old_owner = item.owner.clone();2129 item.owner = new_owner.clone();2130 <NftItemList<T>>::insert(collection_id, item_id, item);21312132 // update index collection2133 Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21342135 collection.log(ERC721Events::Transfer {2136 from: *sender.as_eth(),2137 to: *new_owner.as_eth(),2138 token_id: item_id.into(),2139 })?;2140 Self::deposit_event(RawEvent::Transfer(2141 collection.id,2142 item_id,2143 sender,2144 new_owner,2145 1,2146 ));21472148 Ok(())2149 }21502151 fn set_re_fungible_variable_data(2152 collection: &CollectionHandle<T>,2153 item_id: TokenId,2154 data: Vec<u8>,2155 ) -> DispatchResult {2156 let collection_id = collection.id;2157 let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2158 .ok_or(Error::<T>::TokenNotFound)?;21592160 item.variable_data = data;21612162 <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21632164 Ok(())2165 }21662167 fn set_nft_variable_data(2168 collection: &CollectionHandle<T>,2169 item_id: TokenId,2170 data: Vec<u8>,2171 ) -> DispatchResult {2172 let collection_id = collection.id;2173 let mut item =2174 <NftItemList<T>>::get(collection_id, item_id).ok_or(Error::<T>::TokenNotFound)?;21752176 item.variable_data = data;21772178 <NftItemList<T>>::insert(collection_id, item_id, item);21792180 Ok(())2181 }21822183 #[allow(dead_code)]2184 fn init_collection(item: &Collection<T>) {2185 // check params2186 assert!(2187 item.decimal_points <= MAX_DECIMAL_POINTS,2188 "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2189 );2190 assert!(2191 item.name.len() <= 64,2192 "Collection name can not be longer than 63 char"2193 );2194 assert!(2195 item.name.len() <= 256,2196 "Collection description can not be longer than 255 char"2197 );2198 assert!(2199 item.token_prefix.len() <= 16,2200 "Token prefix can not be longer than 15 char"2201 );22022203 // Generate next collection ID2204 let next_id = CreatedCollectionCount::get().checked_add(1).unwrap();22052206 CreatedCollectionCount::put(next_id);2207 }22082209 #[allow(dead_code)]2210 fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2211 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22122213 Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22142215 <ItemListIndex>::insert(collection_id, current_index);22162217 // Update balance2218 let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2219 .checked_add(1)2220 .unwrap();2221 <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2222 }22232224 #[allow(dead_code)]2225 fn init_fungible_token(2226 collection_id: CollectionId,2227 owner: &T::CrossAccountId,2228 item: &FungibleItemType,2229 ) {2230 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();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(item.value)2239 .unwrap();2240 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2241 }22422243 #[allow(dead_code)]2244 fn init_refungible_token(2245 collection_id: CollectionId,2246 item: &ReFungibleItemType<T::CrossAccountId>,2247 ) {2248 let current_index = <ItemListIndex>::get(collection_id).checked_add(1).unwrap();22492250 let value = item.owner.first().unwrap().fraction;2251 let owner = item.owner.first().unwrap().owner.clone();22522253 Self::add_token_index(collection_id, current_index, &owner).unwrap();22542255 <ItemListIndex>::insert(collection_id, current_index);22562257 // Update balance2258 let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2259 .checked_add(value)2260 .unwrap();2261 <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2262 }22632264 fn add_token_index(2265 collection_id: CollectionId,2266 item_index: TokenId,2267 owner: &T::CrossAccountId,2268 ) -> DispatchResult {2269 // add to account limit2270 if <AccountItemCount<T>>::contains_key(owner.as_sub()) {2271 // bound Owned tokens by a single address2272 let count = <AccountItemCount<T>>::get(owner.as_sub());2273 ensure!(2274 count < ACCOUNT_TOKEN_OWNERSHIP_LIMIT,2275 Error::<T>::AddressOwnershipLimitExceeded2276 );22772278 <AccountItemCount<T>>::insert(2279 owner.as_sub(),2280 count.checked_add(1).ok_or(Error::<T>::NumOverflow)?,2281 );2282 } else {2283 <AccountItemCount<T>>::insert(owner.as_sub(), 1);2284 }22852286 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2287 if list_exists {2288 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2289 let item_contains = list.contains(&item_index.clone());22902291 if !item_contains {2292 list.push(item_index);2293 }22942295 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2296 } else {2297 let itm = vec![item_index];2298 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2299 }23002301 Ok(())2302 }23032304 fn remove_token_index(2305 collection_id: CollectionId,2306 item_index: TokenId,2307 owner: &T::CrossAccountId,2308 ) -> DispatchResult {2309 // update counter2310 <AccountItemCount<T>>::insert(2311 owner.as_sub(),2312 <AccountItemCount<T>>::get(owner.as_sub())2313 .checked_sub(1)2314 .ok_or(Error::<T>::NumOverflow)?,2315 );23162317 let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2318 if list_exists {2319 let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2320 let item_contains = list.contains(&item_index.clone());23212322 if item_contains {2323 list.retain(|&item| item != item_index);2324 <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2325 }2326 }23272328 Ok(())2329 }23302331 fn move_token_index(2332 collection_id: CollectionId,2333 item_index: TokenId,2334 old_owner: &T::CrossAccountId,2335 new_owner: &T::CrossAccountId,2336 ) -> DispatchResult {2337 Self::remove_token_index(collection_id, item_index, old_owner)?;2338 Self::add_token_index(collection_id, item_index, new_owner)?;23392340 Ok(())2341 }2342}23432344sp_api::decl_runtime_apis! {2345 pub trait NftApi {2346 /// Used for ethereum integration2347 fn eth_contract_code(account: H160) -> Option<Vec<u8>>;2348 }2349}pallets/nft/src/sponsorship.rsdiffbeforeafterboth--- a/pallets/nft/src/sponsorship.rs
+++ b/pallets/nft/src/sponsorship.rs
@@ -1,22 +1,25 @@
use crate::{
Config, Call, CollectionById, CreateItemBasket, VariableMetaDataBasket,
- ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket,
- CreateItemData, CollectionMode, limit,
+ ReFungibleTransferBasket, FungibleTransferBasket, NftTransferBasket, CreateItemData,
+ CollectionMode,
};
use core::marker::PhantomData;
use up_sponsorship::SponsorshipHandler;
use frame_support::{
- traits::{IsSubType, Get},
+ traits::{IsSubType},
storage::{StorageMap, StorageDoubleMap},
};
-use nft_data_structs::{TokenId, CollectionId};
+use nft_data_structs::{
+ TokenId, CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+ FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT,
+};
pub struct NftSponsorshipHandler<T>(PhantomData<T>);
impl<T: Config> NftSponsorshipHandler<T> {
pub fn withdraw_create_item(
who: &T::AccountId,
collection_id: &CollectionId,
- _properties: &CreateItemData<T::ChainLimits>,
+ _properties: &CreateItemData,
) -> Option<T::AccountId> {
let collection = CollectionById::<T>::get(collection_id)?;
@@ -61,7 +64,7 @@
let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
collection_limits.sponsor_transfer_timeout
} else {
- <limit!(T, NftSponsorTransferTimeout)>::get()
+ NFT_SPONSOR_TRANSFER_TIMEOUT
};
let mut sponsored = true;
@@ -83,7 +86,7 @@
let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
collection_limits.sponsor_transfer_timeout
} else {
- <limit!(T, FungibleSponsorTransferTimeout)>::get()
+ FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
};
let block_number = <frame_system::Pallet<T>>::block_number() as T::BlockNumber;
@@ -106,7 +109,7 @@
let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {
collection_limits.sponsor_transfer_timeout
} else {
- <limit!(T, ReFungibleSponsorTransferTimeout)>::get()
+ REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT
};
let mut sponsored = true;
primitives/nft/src/lib.rsdiffbeforeafterboth--- a/primitives/nft/src/lib.rs
+++ b/primitives/nft/src/lib.rs
@@ -28,6 +28,29 @@
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_ADMINS_LIMIT: u64 = 5;
+pub const ACCOUNT_TOKEN_OWNERSHIP_LIMIT: u32 = 1000000;
+
+// Timeouts for item types in passed blocks
+pub const NFT_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+pub const FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+pub const REFUNGIBLE_SPONSOR_TRANSFER_TIMEOUT: u32 = 5;
+
+// Schema limits
+pub const OFFCHAIN_SCHEMA_LIMIT: u32 = 1024;
+pub const VARIABLE_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
+pub const CONST_ON_CHAIN_SCHEMA_LIMIT: u32 = 1024;
+
+/// How much items can be created per single
+/// create_many call
+pub const MAX_ITEMS_PER_BATCH: u32 = 200;
+
+parameter_types! {
+ pub const CustomDataLimit: u32 = CUSTOM_DATA_LIMIT;
+}
+
pub type CollectionId = u32;
pub type TokenId = u32;
pub type DecimalPoints = u8;
@@ -203,27 +226,6 @@
}
}
-pub trait ChainLimits {
- type CollectionNumberLimit: Get<u32>;
- type AccountTokenOwnershipLimit: Get<u32>;
- type CollectionAdminsLimit: Get<u64>;
- type CustomDataLimit: Get<u32>;
-
- // Timeouts for item types in passed blocks
- type NftSponsorTransferTimeout: Get<u32>;
- type FungibleSponsorTransferTimeout: Get<u32>;
- type ReFungibleSponsorTransferTimeout: Get<u32>;
-
- // Schema limits
- type OffchainSchemaLimit: Get<u32>;
- type VariableOnChainSchemaLimit: Get<u32>;
- type ConstOnChainSchemaLimit: Get<u32>;
-
- /// How much items can be created per single
- /// create_many call
- type MaxItemsPerBatch: Get<u32>;
-}
-
/// BoundedVec doesn't supports serde
#[cfg(feature = "serde1")]
mod bounded_serde {
@@ -257,16 +259,16 @@
}
}
-#[derive(Encode, Decode, MaxEncodedLen, Default, Derivative)]
+#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-#[derivative(Debug(bound = ""), PartialEq(bound = ""), Clone(bound = ""))]
-pub struct CreateNftData<T: ChainLimits> {
+#[derivative(Debug)]
+pub struct CreateNftData {
#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
#[derivative(Debug = "ignore")]
- pub const_data: BoundedVec<u8, T::CustomDataLimit>,
+ pub const_data: BoundedVec<u8, CustomDataLimit>,
#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
#[derivative(Debug = "ignore")]
- pub variable_data: BoundedVec<u8, T::CustomDataLimit>,
+ pub variable_data: BoundedVec<u8, CustomDataLimit>,
}
#[derive(Encode, Decode, MaxEncodedLen, Default, Debug, Clone, PartialEq)]
@@ -275,29 +277,28 @@
pub value: u128,
}
-#[derive(Encode, Decode, MaxEncodedLen, Default, Derivative)]
+#[derive(Encode, Decode, MaxEncodedLen, Default, PartialEq, Clone, Derivative)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-#[derivative(Debug(bound = ""), PartialEq(bound = ""), Clone(bound = ""))]
-pub struct CreateReFungibleData<T: ChainLimits> {
+#[derivative(Debug)]
+pub struct CreateReFungibleData {
#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
#[derivative(Debug = "ignore")]
- pub const_data: BoundedVec<u8, T::CustomDataLimit>,
+ pub const_data: BoundedVec<u8, CustomDataLimit>,
#[cfg_attr(feature = "serde1", serde(with = "bounded_serde"))]
#[derivative(Debug = "ignore")]
- pub variable_data: BoundedVec<u8, T::CustomDataLimit>,
+ pub variable_data: BoundedVec<u8, CustomDataLimit>,
pub pieces: u128,
}
-#[derive(Encode, Decode, MaxEncodedLen, Derivative)]
+#[derive(Encode, Decode, MaxEncodedLen, PartialEq, Clone, Debug)]
#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
-#[derivative(Debug(bound = ""), PartialEq(bound = ""), Clone(bound = ""))]
-pub enum CreateItemData<T: ChainLimits> {
- NFT(CreateNftData<T>),
+pub enum CreateItemData {
+ NFT(CreateNftData),
Fungible(CreateFungibleData),
- ReFungible(CreateReFungibleData<T>),
+ ReFungible(CreateReFungibleData),
}
-impl<T: ChainLimits> CreateItemData<T> {
+impl CreateItemData {
pub fn data_size(&self) -> usize {
match self {
CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),
@@ -307,19 +308,19 @@
}
}
-impl<T: ChainLimits> From<CreateNftData<T>> for CreateItemData<T> {
- fn from(item: CreateNftData<T>) -> Self {
+impl From<CreateNftData> for CreateItemData {
+ fn from(item: CreateNftData) -> Self {
CreateItemData::NFT(item)
}
}
-impl<T: ChainLimits> From<CreateReFungibleData<T>> for CreateItemData<T> {
- fn from(item: CreateReFungibleData<T>) -> Self {
+impl From<CreateReFungibleData> for CreateItemData {
+ fn from(item: CreateReFungibleData) -> Self {
CreateItemData::ReFungible(item)
}
}
-impl<T: ChainLimits> From<CreateFungibleData> for CreateItemData<T> {
+impl From<CreateFungibleData> for CreateItemData {
fn from(item: CreateFungibleData) -> Self {
CreateItemData::Fungible(item)
}
runtime/src/lib.rsdiffbeforeafterboth--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -683,35 +683,6 @@
}
parameter_types! {
- pub const CollectionNumberLimit: u32 = 100000;
- pub const AccountTokenOwnershipLimit: u32 = 1000000;
- pub const CollectionAdminsLimit: u64 = 5;
- pub const CustomDataLimit: u32 = 2048;
- pub const NftSponsorTransferTimeout: u32 = 5;
- pub const FungibleSponsorTransferTimeout: u32 = 5;
- pub const ReFungibleSponsorTransferTimeout: u32 = 5;
- pub const OffchainSchemaLimit: u32 = 1024;
- pub const VariableOnChainSchemaLimit: u32 = 1024;
- pub const ConstOnChainSchemaLimit: u32 = 1024;
- pub const MaxItemsPerBatch: u32 = 200;
-}
-
-pub struct ChainLimits;
-impl nft_data_structs::ChainLimits for ChainLimits {
- type CollectionNumberLimit = CollectionNumberLimit;
- type AccountTokenOwnershipLimit = AccountTokenOwnershipLimit;
- type CollectionAdminsLimit = CollectionAdminsLimit;
- type CustomDataLimit = CustomDataLimit;
- type NftSponsorTransferTimeout = NftSponsorTransferTimeout;
- type FungibleSponsorTransferTimeout = FungibleSponsorTransferTimeout;
- type ReFungibleSponsorTransferTimeout = ReFungibleSponsorTransferTimeout;
- type OffchainSchemaLimit = OffchainSchemaLimit;
- type VariableOnChainSchemaLimit = VariableOnChainSchemaLimit;
- type ConstOnChainSchemaLimit = ConstOnChainSchemaLimit;
- type MaxItemsPerBatch = MaxItemsPerBatch;
-}
-
-parameter_types! {
pub TreasuryAccountId: AccountId = TreasuryModuleId::get().into_account();
pub const CollectionCreationPrice: Balance = 100 * UNIQUE;
}
@@ -728,7 +699,6 @@
type Currency = Balances;
type CollectionCreationPrice = CollectionCreationPrice;
type TreasuryAccountId = TreasuryAccountId;
- type ChainLimits = ChainLimits;
}
parameter_types! {