git.delta.rocks / unique-network / refs/commits / 481e942f67c4

difftreelog

feat support transfers from evm

Yaroslav Bolyukin2021-04-30parent: #f2db9e8.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16    construct_runtime, decl_event, decl_module, decl_storage, decl_error,17    dispatch::DispatchResult,18    ensure, fail, parameter_types,19    traits::{20        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21        Randomness, IsSubType, WithdrawReasons,22    },23    weights::{24        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26        WeightToFeePolynomial, DispatchClass,27    },28    StorageValue,29    transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_evm::AddressMapping;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::account::*;5960pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;61pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;62pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;63pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6465// Structs66// #region6768pub type CollectionId = u32;69pub type TokenId = u32;70pub type DecimalPoints = u8;7172#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]73#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]74pub enum CollectionMode {75    Invalid,76    NFT,77    // decimal points78    Fungible(DecimalPoints),79    ReFungible,80}8182impl Default for CollectionMode {83    fn default() -> Self {84        Self::Invalid85    }86}8788impl Into<u8> for CollectionMode {89    fn into(self) -> u8 {90        match self {91            CollectionMode::Invalid => 0,92            CollectionMode::NFT => 1,93            CollectionMode::Fungible(_) => 2,94            CollectionMode::ReFungible => 3,95        }96    }97}9899#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub enum AccessMode {102    Normal,103    WhiteList,104}105impl Default for AccessMode {106    fn default() -> Self {107        Self::Normal108    }109}110111#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub enum SchemaVersion {114    ImageURL,115    Unique,116}117impl Default for SchemaVersion {118    fn default() -> Self {119        Self::ImageURL120    }121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct Ownership<AccountId> {126    pub owner: AccountId,127    pub fraction: u128,128}129130#[derive(Encode, Decode, Debug, Clone, PartialEq)]131#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]132pub enum SponsorshipState<AccountId> {133    /// The fees are applied to the transaction sender134    Disabled,135    Unconfirmed(AccountId),136    /// Transactions are sponsored by specified account137    Confirmed(AccountId),138}139140impl<AccountId> SponsorshipState<AccountId> {141    fn sponsor(&self) -> Option<&AccountId> {142        match self {143            Self::Confirmed(sponsor) => Some(sponsor),144            _ => None,145        }146    }147148    fn pending_sponsor(&self) -> Option<&AccountId> {149        match self {150            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),151            _ => None,152        }153    }154155    fn confirmed(&self) -> bool {156        matches!(self, Self::Confirmed(_))157    }158}159160impl<T> Default for SponsorshipState<T> {161    fn default() -> Self {162        Self::Disabled163    }164}165166#[derive(Encode, Decode, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct Collection<T: Config> {169    pub owner: T::CrossAccountId,170    pub mode: CollectionMode,171    pub access: AccessMode,172    pub decimal_points: DecimalPoints,173    pub name: Vec<u16>,        // 64 include null escape char174    pub description: Vec<u16>, // 256 include null escape char175    pub token_prefix: Vec<u8>, // 16 include null escape char176    pub mint_mode: bool,177    pub offchain_schema: Vec<u8>,178    pub schema_version: SchemaVersion,179    pub sponsorship: SponsorshipState<T::AccountId>,180    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 181    pub variable_on_chain_schema: Vec<u8>, //182    pub const_on_chain_schema: Vec<u8>, //183}184185pub struct CollectionHandle<T: Config> {186    pub id: CollectionId,187    collection: Collection<T>,188}189190impl<T: Config> Deref for CollectionHandle<T> {191    type Target = Collection<T>;192193    fn deref(&self) -> &Self::Target {194        &self.collection195    }196}197198impl<T: Config> DerefMut for CollectionHandle<T> {199    fn deref_mut(&mut self) -> &mut Self::Target {200        &mut self.collection201    }202}203204#[derive(Encode, Decode, Debug, Clone, PartialEq)]205#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206pub struct NftItemType<AccountId> {207    pub owner: AccountId,208    pub const_data: Vec<u8>,209    pub variable_data: Vec<u8>,210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct FungibleItemType {215    pub value: u128,216}217218#[derive(Encode, Decode, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct ReFungibleItemType<AccountId> {221    pub owner: Vec<Ownership<AccountId>>,222    pub const_data: Vec<u8>,223    pub variable_data: Vec<u8>,224}225226// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228// pub struct VestingItem<AccountId, Moment> {229//     pub sender: AccountId,230//     pub recipient: AccountId,231//     pub collection_id: CollectionId,232//     pub item_id: TokenId,233//     pub amount: u64,234//     pub vesting_date: Moment,235// }236237#[derive(Encode, Decode, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct CollectionLimits<BlockNumber: Encode + Decode> {240    pub account_token_ownership_limit: u32,241    pub sponsored_data_size: u32,242    /// None - setVariableMetadata is not sponsored243    /// Some(v) - setVariableMetadata is sponsored 244    ///           if there is v block between txs245    pub sponsored_data_rate_limit: Option<BlockNumber>,246    pub token_limit: u32,247248    // Timeouts for item types in passed blocks249    pub sponsor_transfer_timeout: u32,250    pub owner_can_transfer: bool,251    pub owner_can_destroy: bool,252}253254impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {255    fn default() -> Self {256        Self { 257            account_token_ownership_limit: 10_000_000, 258            token_limit: u32::max_value(),259            sponsored_data_size: u32::MAX, 260            sponsored_data_rate_limit: None,261            sponsor_transfer_timeout: 14400,262            owner_can_transfer: true,263            owner_can_destroy: true264        }265    }266}267268#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]269#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]270pub struct ChainLimits {271    pub collection_numbers_limit: u32,272    pub account_token_ownership_limit: u32,273    pub collections_admins_limit: u64,274    pub custom_data_limit: u32,275276    // Timeouts for item types in passed blocks277    pub nft_sponsor_transfer_timeout: u32,278    pub fungible_sponsor_transfer_timeout: u32,279    pub refungible_sponsor_transfer_timeout: u32,280281    // Schema limits282    pub offchain_schema_limit: u32,283    pub variable_on_chain_schema_limit: u32,284    pub const_on_chain_schema_limit: u32,285}286287pub trait WeightInfo {288	fn create_collection() -> Weight;289	fn destroy_collection() -> Weight;290	fn add_to_white_list() -> Weight;291	fn remove_from_white_list() -> Weight;292    fn set_public_access_mode() -> Weight;293    fn set_mint_permission() -> Weight;294    fn change_collection_owner() -> Weight;295    fn add_collection_admin() -> Weight;296    fn remove_collection_admin() -> Weight;297    fn set_collection_sponsor() -> Weight;298    fn confirm_sponsorship() -> Weight;299    fn remove_collection_sponsor() -> Weight;300    fn create_item(s: usize) -> Weight;301    fn burn_item() -> Weight;302    fn transfer() -> Weight;303    fn approve() -> Weight;304    fn transfer_from() -> Weight;305    fn set_offchain_schema() -> Weight;306    fn set_const_on_chain_schema() -> Weight;307    fn set_variable_on_chain_schema() -> Weight;308    fn set_variable_meta_data() -> Weight;309    fn enable_contract_sponsoring() -> Weight;310    fn set_schema_version() -> Weight;311    fn set_chain_limits() -> Weight;312    fn set_contract_sponsoring_rate_limit() -> Weight;313    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;314    fn toggle_contract_white_list() -> Weight;315    fn add_to_contract_white_list() -> Weight;316    fn remove_from_contract_white_list() -> Weight;317    fn set_collection_limits() -> Weight;318}319320#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]321#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]322pub struct CreateNftData {323    pub const_data: Vec<u8>,324    pub variable_data: Vec<u8>,325}326327#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]328#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]329pub struct CreateFungibleData {330    pub value: u128,331}332333#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]334#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]335pub struct CreateReFungibleData {336    pub const_data: Vec<u8>,337    pub variable_data: Vec<u8>,338    pub pieces: u128,339}340341#[derive(Encode, Decode, Debug, Clone, PartialEq)]342#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]343pub enum CreateItemData {344    NFT(CreateNftData),345    Fungible(CreateFungibleData),346    ReFungible(CreateReFungibleData),347}348349impl CreateItemData {350    pub fn len(&self) -> usize {351        let len = match self {352            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),353            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),354            _ => 0355        };356        357        return len;358    }359}360361impl From<CreateNftData> for CreateItemData {362    fn from(item: CreateNftData) -> Self {363        CreateItemData::NFT(item)364    }365}366367impl From<CreateReFungibleData> for CreateItemData {368    fn from(item: CreateReFungibleData) -> Self {369        CreateItemData::ReFungible(item)370    }371}372373impl From<CreateFungibleData> for CreateItemData {374    fn from(item: CreateFungibleData) -> Self {375        CreateItemData::Fungible(item)376    }377}378379380decl_error! {381	/// Error for non-fungible-token module.382	pub enum Error for Module<T: Config> {383        /// Total collections bound exceeded.384        TotalCollectionsLimitExceeded,385		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.386        CollectionDecimalPointLimitExceeded, 387        /// Collection name can not be longer than 63 char.388        CollectionNameLimitExceeded, 389        /// Collection description can not be longer than 255 char.390        CollectionDescriptionLimitExceeded, 391        /// Token prefix can not be longer than 15 char.392        CollectionTokenPrefixLimitExceeded,393        /// This collection does not exist.394        CollectionNotFound,395        /// Item not exists.396        TokenNotFound,397        /// Admin not found398        AdminNotFound,399        /// Arithmetic calculation overflow.400        NumOverflow,       401        /// Account already has admin role.402        AlreadyAdmin,  403        /// You do not own this collection.404        NoPermission,405        /// This address is not set as sponsor, use setCollectionSponsor first.406        ConfirmUnsetSponsorFail,407        /// Collection is not in mint mode.408        PublicMintingNotAllowed,409        /// Sender parameter and item owner must be equal.410        MustBeTokenOwner,411        /// Item balance not enough.412        TokenValueTooLow,413        /// Size of item is too large.414        NftSizeLimitExceeded,415        /// No approve found416        ApproveNotFound,417        /// Requested value more than approved.418        TokenValueNotEnough,419        /// Only approved addresses can call this method.420        ApproveRequired,421        /// Address is not in white list.422        AddresNotInWhiteList,423        /// Number of collection admins bound exceeded.424        CollectionAdminsLimitExceeded,425        /// Owned tokens by a single address bound exceeded.426        AddressOwnershipLimitExceeded,427        /// Length of items properties must be greater than 0.428        EmptyArgument,429        /// const_data exceeded data limit.430        TokenConstDataLimitExceeded,431        /// variable_data exceeded data limit.432        TokenVariableDataLimitExceeded,433        /// Not NFT item data used to mint in NFT collection.434        NotNftDataUsedToMintNftCollectionToken,435        /// Not Fungible item data used to mint in Fungible collection.436        NotFungibleDataUsedToMintFungibleCollectionToken,437        /// Not Re Fungible item data used to mint in Re Fungible collection.438        NotReFungibleDataUsedToMintReFungibleCollectionToken,439        /// Unexpected collection type.440        UnexpectedCollectionType,441        /// Can't store metadata in fungible tokens.442        CantStoreMetadataInFungibleTokens,443        /// Collection token limit exceeded444        CollectionTokenLimitExceeded,445        /// Account token limit exceeded per collection446        AccountTokenLimitExceeded,447        /// Collection limit bounds per collection exceeded448        CollectionLimitBoundsExceeded,449        /// Tried to enable permissions which are only permitted to be disabled450        OwnerPermissionsCantBeReverted,451        /// Schema data size limit bound exceeded452        SchemaDataLimitExceeded,453        /// Maximum refungibility exceeded454        WrongRefungiblePieces,455        /// createRefungible should be called with one owner456        BadCreateRefungibleCall,457	}458}459460pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {461    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;462463    /// Weight information for extrinsics in this pallet.464	type WeightInfo: WeightInfo;465466    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;467    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;468    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;469470	type CrossAccountId: CrossAccountId<Self::AccountId>;471    type Currency: Currency<Self::AccountId>;472    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;473    type TreasuryAccountId: Get<Self::AccountId>;474}475476#[cfg(feature = "runtime-benchmarks")]477mod benchmarking;478479// #endregion480481// # Used definitions482//483// ## User control levels484//485// chain-controlled - key is uncontrolled by user486//                    i.e autoincrementing index487//                    can use non-cryptographic hash488// real - key is controlled by user489//        but it is hard to generate enough colliding values, i.e owner of signed txs490//        can use non-cryptographic hash491// controlled - key is completly controlled by users492//              i.e maps with mutable keys493//              should use cryptographic hash494//495// ## User control level downgrade reasons496//497// ?1 - chain-controlled -> controlled498//      collections/tokens can be destroyed, resulting in massive holes499// ?2 - chain-controlled -> controlled500//      same as ?1, but can be only added, resulting in easier exploitation501// ?3 - real -> controlled502//      no confirmation required, so addresses can be easily generated503decl_storage! {504    trait Store for Module<T: Config> as Nft {505506        //#region Private members507        /// Id of next collection508        CreatedCollectionCount: u32;509        /// Used for migrations510        ChainVersion: u64;511        /// Id of last collection token512        /// Collection id (controlled?1)513        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;514        //#endregion515516        //#region Chain limits struct517        pub ChainLimit get(fn chain_limit) config(): ChainLimits;518        //#endregion519520        //#region Bound counters521        /// Amount of collections destroyed, used for total amount tracking with522        /// CreatedCollectionCount523        DestroyedCollectionCount: u32;524        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)525        /// Account id (real)526        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;527        //#endregion528529        //#region Basic collections530        /// Collection info531        /// Collection id (controlled?1)532        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;533        /// List of collection admins534        /// Collection id (controlled?2)535        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;536        /// Whitelisted collection users537        /// Collection id (controlled?2), user id (controlled?3)538        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;539        //#endregion540541        /// How many of collection items user have542        /// Collection id (controlled?2), account id (real)543        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;544545        /// Amount of items which spender can transfer out of owners account (via transferFrom)546        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))547        /// TODO: Off chain worker should remove from this map when token gets removed548        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;549550        //#region Item collections551        /// Collection id (controlled?2), token id (controlled?1)552        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;553        /// Collection id (controlled?2), owner (controlled?2)554        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;555        /// Collection id (controlled?2), token id (controlled?1)556        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;557        //#endregion558559        //#region Index list560        /// Collection id (controlled?2), tokens owner (controlled?2)561        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;562        //#endregion563564        //#region Tokens transfer rate limit baskets565        /// (Collection id (controlled?2), who created (real))566        /// TODO: Off chain worker should remove from this map when collection gets removed567        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;568        /// Collection id (controlled?2), token id (controlled?2)569        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;570        /// Collection id (controlled?2), owning user (real)571        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;572        /// Collection id (controlled?2), token id (controlled?2)573        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;574        //#endregion575576        /// Variable metadata sponsoring577        /// Collection id (controlled?2), token id (controlled?2)578        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;579      580        //#region Contract Sponsorship and Ownership581        /// Contract address (real)582        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;583        /// Contract address (real)584        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;585        /// (Contract address(real), caller (real))586        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;587        /// Contract address (real)588        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;589        /// Contract address (real)590        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 591        /// Contract address (real) => Whitelisted user (controlled?3)592        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 593        //#endregion594    }595    add_extra_genesis {596        build(|config: &GenesisConfig<T>| {597            // Modification of storage598            for (_num, _c) in &config.collection_id {599                <Module<T>>::init_collection(_c);600            }601602            for (_num, _c, _i) in &config.nft_item_id {603                <Module<T>>::init_nft_token(*_c, _i);604            }605606            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {607                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);608            }609610            for (_num, _c, _i) in &config.refungible_item_id {611                <Module<T>>::init_refungible_token(*_c, _i);612            }613        })614    }615}616617decl_event!(618    pub enum Event<T>619    where620        CrossAccountId = <T as Config>::CrossAccountId,621    {622        /// New collection was created623        /// 624        /// # Arguments625        /// 626        /// * collection_id: Globally unique identifier of newly created collection.627        /// 628        /// * mode: [CollectionMode] converted into u8.629        /// 630        /// * account_id: Collection owner.631        CollectionCreated(CollectionId, u8, CrossAccountId),632633        /// New item was created.634        /// 635        /// # Arguments636        /// 637        /// * collection_id: Id of the collection where item was created.638        /// 639        /// * item_id: Id of an item. Unique within the collection.640        ///641        /// * recipient: Owner of newly created item 642        ItemCreated(CollectionId, TokenId, CrossAccountId),643644        /// Collection item was burned.645        /// 646        /// # Arguments647        /// 648        /// collection_id.649        /// 650        /// item_id: Identifier of burned NFT.651        ItemDestroyed(CollectionId, TokenId),652653        /// Item was transferred654        ///655        /// * collection_id: Id of collection to which item is belong656        ///657        /// * item_id: Id of an item658        ///659        /// * sender: Original owner of item660        ///661        /// * recipient: New owner of item662        ///663        /// * amount: Always 1 for NFT664        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),665666        /// * collection_id667        ///668        /// * item_id669        ///670        /// * sender671        ///672        /// * spender673        ///674        /// * amount675        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),676    }677);678679decl_module! {680    pub struct Module<T: Config> for enum Call 681    where 682        origin: T::Origin683    {684        fn deposit_event() = default;685        type Error = Error<T>;686687        fn on_initialize(now: T::BlockNumber) -> Weight {688            0689        }690691        /// 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.692        /// 693        /// # Permissions694        /// 695        /// * Anyone.696        /// 697        /// # Arguments698        /// 699        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.700        /// 701        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.702        /// 703        /// * token_prefix: UTF-8 string with token prefix.704        /// 705        /// * mode: [CollectionMode] collection type and type dependent data.706        // returns collection ID707        #[weight = <T as Config>::WeightInfo::create_collection()]708        #[transactional]709        pub fn create_collection(origin,710                                 collection_name: Vec<u16>,711                                 collection_description: Vec<u16>,712                                 token_prefix: Vec<u8>,713                                 mode: CollectionMode) -> DispatchResult {714715            // Anyone can create a collection716            let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);717718            // Take a (non-refundable) deposit of collection creation719            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();720            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(721                &T::TreasuryAccountId::get(),722                T::CollectionCreationPrice::get(),723            ));724            <T as Config>::Currency::settle(725                who.as_sub(),726                imbalance,727                WithdrawReasons::TRANSFER,728                ExistenceRequirement::KeepAlive,729            ).map_err(|_| Error::<T>::NoPermission)?;730731            let decimal_points = match mode {732                CollectionMode::Fungible(points) => points,733                _ => 0734            };735736            let chain_limit = ChainLimit::get();737738            let created_count = CreatedCollectionCount::get();739            let destroyed_count = DestroyedCollectionCount::get();740741            // bound Total number of collections742            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);743744            // check params745            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);746            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);747            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);748            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);749750            // Generate next collection ID751            let next_id = created_count752                .checked_add(1)753                .ok_or(Error::<T>::NumOverflow)?;754755            CreatedCollectionCount::put(next_id);756757            let limits = CollectionLimits {758                sponsored_data_size: chain_limit.custom_data_limit,759                ..Default::default()760            };761762            // Create new collection763            let new_collection = Collection {764                owner: who.clone(),765                name: collection_name,766                mode: mode.clone(),767                mint_mode: false,768                access: AccessMode::Normal,769                description: collection_description,770                decimal_points: decimal_points,771                token_prefix: token_prefix,772                offchain_schema: Vec::new(),773                schema_version: SchemaVersion::ImageURL,774                sponsorship: SponsorshipState::Disabled,775                variable_on_chain_schema: Vec::new(),776                const_on_chain_schema: Vec::new(),777                limits,778            };779780            // Add new collection to map781            <CollectionById<T>>::insert(next_id, new_collection);782783            // call event784            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));785786            Ok(())787        }788789        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.790        /// 791        /// # Permissions792        /// 793        /// * Collection Owner.794        /// 795        /// # Arguments796        /// 797        /// * collection_id: collection to destroy.798        #[weight = <T as Config>::WeightInfo::destroy_collection()]799        #[transactional]800        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {801802            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);803            let collection = Self::get_collection(collection_id)?;804            Self::check_owner_permissions(&collection, sender)?;805            if !collection.limits.owner_can_destroy {806                fail!(Error::<T>::NoPermission);807            }808809            <AddressTokens<T>>::remove_prefix(collection_id);810            <Allowances<T>>::remove_prefix(collection_id);811            <Balance<T>>::remove_prefix(collection_id);812            <ItemListIndex>::remove(collection_id);813            <AdminList<T>>::remove(collection_id);814            <CollectionById<T>>::remove(collection_id);815            <WhiteList<T>>::remove_prefix(collection_id);816817            <NftItemList<T>>::remove_prefix(collection_id);818            <FungibleItemList<T>>::remove_prefix(collection_id);819            <ReFungibleItemList<T>>::remove_prefix(collection_id);820821            <NftTransferBasket<T>>::remove_prefix(collection_id);822            <FungibleTransferBasket<T>>::remove_prefix(collection_id);823            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);824825            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);826827            DestroyedCollectionCount::put(DestroyedCollectionCount::get()828                .checked_add(1)829                .ok_or(Error::<T>::NumOverflow)?);830831            Ok(())832        }833834        /// Add an address to white list.835        /// 836        /// # Permissions837        /// 838        /// * Collection Owner839        /// * Collection Admin840        /// 841        /// # Arguments842        /// 843        /// * collection_id.844        /// 845        /// * address.846        #[weight = <T as Config>::WeightInfo::add_to_white_list()]847        #[transactional]848        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{849850            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851            let collection = Self::get_collection(collection_id)?;852            Self::check_owner_or_admin_permissions(&collection, sender)?;853854            <WhiteList<T>>::insert(collection_id, address.as_sub(), true);855            856            Ok(())857        }858859        /// Remove an address from white list.860        /// 861        /// # Permissions862        /// 863        /// * Collection Owner864        /// * Collection Admin865        /// 866        /// # Arguments867        /// 868        /// * collection_id.869        /// 870        /// * address.871        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]872        #[transactional]873        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{874875            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);876            let collection = Self::get_collection(collection_id)?;877            Self::check_owner_or_admin_permissions(&collection, sender)?;878879            <WhiteList<T>>::remove(collection_id, address.as_sub());880881            Ok(())882        }883884        /// Toggle between normal and white list access for the methods with access for `Anyone`.885        /// 886        /// # Permissions887        /// 888        /// * Collection Owner.889        /// 890        /// # Arguments891        /// 892        /// * collection_id.893        /// 894        /// * mode: [AccessMode]895        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]896        #[transactional]897        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult898        {899            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900901            let mut target_collection = Self::get_collection(collection_id)?;902            Self::check_owner_permissions(&target_collection, sender)?;903            target_collection.access = mode;904            Self::save_collection(target_collection);905906            Ok(())907        }908909        /// Allows Anyone to create tokens if:910        /// * White List is enabled, and911        /// * Address is added to white list, and912        /// * This method was called with True parameter913        /// 914        /// # Permissions915        /// * Collection Owner916        ///917        /// # Arguments918        /// 919        /// * collection_id.920        /// 921        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.922        #[weight = <T as Config>::WeightInfo::set_mint_permission()]923        #[transactional]924        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult925        {926            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);927928            let mut target_collection = Self::get_collection(collection_id)?;929            Self::check_owner_permissions(&target_collection, sender)?;930            target_collection.mint_mode = mint_permission;931            Self::save_collection(target_collection);932933            Ok(())934        }935936        /// Change the owner of the collection.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner.941        /// 942        /// # Arguments943        /// 944        /// * collection_id.945        /// 946        /// * new_owner.947        #[weight = <T as Config>::WeightInfo::change_collection_owner()]948        #[transactional]949        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {950951            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952            let mut target_collection = Self::get_collection(collection_id)?;953            Self::check_owner_permissions(&target_collection, sender)?;954            target_collection.owner = new_owner;955            Self::save_collection(target_collection);956957            Ok(())958        }959960        /// Adds an admin of the Collection.961        /// 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. 962        /// 963        /// # Permissions964        /// 965        /// * Collection Owner.966        /// * Collection Admin.967        /// 968        /// # Arguments969        /// 970        /// * collection_id: ID of the Collection to add admin for.971        /// 972        /// * new_admin_id: Address of new admin to add.973        #[weight = <T as Config>::WeightInfo::add_collection_admin()]974        #[transactional]975        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {976977            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);978            let collection = Self::get_collection(collection_id)?;979            Self::check_owner_or_admin_permissions(&collection, sender)?;980            let mut admin_arr = <AdminList<T>>::get(collection_id);981982            match admin_arr.binary_search(&new_admin_id) {983                Ok(_) => {},984                Err(idx) => {985                    let limits = ChainLimit::get();986                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);987                    admin_arr.insert(idx, new_admin_id);988                    <AdminList<T>>::insert(collection_id, admin_arr);989                }990            }991            Ok(())992        }993994        /// 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.995        ///996        /// # Permissions997        /// 998        /// * Collection Owner.999        /// * Collection Admin.1000        /// 1001        /// # Arguments1002        /// 1003        /// * collection_id: ID of the Collection to remove admin for.1004        /// 1005        /// * account_id: Address of admin to remove.1006        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1007        #[transactional]1008        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {10091010            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1011            let collection = Self::get_collection(collection_id)?;1012            Self::check_owner_or_admin_permissions(&collection, sender)?;1013            let mut admin_arr = <AdminList<T>>::get(collection_id);10141015            match admin_arr.binary_search(&account_id) {1016                Ok(idx) => {1017                    admin_arr.remove(idx);1018                    <AdminList<T>>::insert(collection_id, admin_arr);1019                },1020                Err(_) => {}1021            }1022            Ok(())1023        }10241025        /// # Permissions1026        /// 1027        /// * Collection Owner1028        /// 1029        /// # Arguments1030        /// 1031        /// * collection_id.1032        /// 1033        /// * new_sponsor.1034        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1035        #[transactional]1036        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1037            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038            let mut target_collection = Self::get_collection(collection_id)?;1039            Self::check_owner_permissions(&target_collection, &sender)?;10401041            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1042            Self::save_collection(target_collection);10431044            Ok(())1045        }10461047        /// # Permissions1048        /// 1049        /// * Sponsor.1050        /// 1051        /// # Arguments1052        /// 1053        /// * collection_id.1054        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1055        #[transactional]1056        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1057            let sender = ensure_signed(origin)?;10581059            let mut target_collection = Self::get_collection(collection_id)?;1060            ensure!(1061                target_collection.sponsorship.pending_sponsor() == Some(&sender),1062                Error::<T>::ConfirmUnsetSponsorFail1063            );10641065            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1066            Self::save_collection(target_collection);10671068            Ok(())1069        }10701071        /// Switch back to pay-per-own-transaction model.1072        ///1073        /// # Permissions1074        ///1075        /// * Collection owner.1076        /// 1077        /// # Arguments1078        /// 1079        /// * collection_id.1080        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1081        #[transactional]1082        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1083            let sender = ensure_signed(origin)?;10841085            let mut target_collection = Self::get_collection(collection_id)?;1086            Self::check_owner_permissions(&target_collection, sender)?;10871088            target_collection.sponsorship = SponsorshipState::Disabled;1089            Self::save_collection(target_collection);10901091            Ok(())1092        }10931094        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1095        /// 1096        /// # Permissions1097        /// 1098        /// * Collection Owner.1099        /// * Collection Admin.1100        /// * Anyone if1101        ///     * White List is enabled, and1102        ///     * Address is added to white list, and1103        ///     * MintPermission is enabled (see SetMintPermission method)1104        /// 1105        /// # Arguments1106        /// 1107        /// * collection_id: ID of the collection.1108        /// 1109        /// * owner: Address, initial owner of the NFT.1110        ///1111        /// * data: Token data to store on chain.1112        // #[weight =1113        // (130_000_000 as Weight)1114        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1115        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1116        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11171118        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1119        #[transactional]1120        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {11211122            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11231124            let target_collection = Self::get_collection(collection_id)?;11251126            Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1127            Self::validate_create_item_args(&target_collection, &data)?;1128            Self::create_item_no_validation(&target_collection, owner, data)?;11291130            Ok(())1131        }11321133        /// This method creates multiple items in a collection created with CreateCollection method.1134        /// 1135        /// # Permissions1136        /// 1137        /// * Collection Owner.1138        /// * Collection Admin.1139        /// * Anyone if1140        ///     * White List is enabled, and1141        ///     * Address is added to white list, and1142        ///     * MintPermission is enabled (see SetMintPermission method)1143        /// 1144        /// # Arguments1145        /// 1146        /// * collection_id: ID of the collection.1147        /// 1148        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1149        /// 1150        /// * owner: Address, initial owner of the NFT.1151        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1152                               .map(|data| { data.len() })1153                               .sum())]1154        #[transactional]1155        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11561157            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1158            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1159            let collection = Self::get_collection(collection_id)?;11601161            Self::create_multiple_items_internal(sender, &collection, owner, items_data)?;11621163            Ok(())1164        }11651166        /// Destroys a concrete instance of NFT.1167        /// 1168        /// # Permissions1169        /// 1170        /// * Collection Owner.1171        /// * Collection Admin.1172        /// * Current NFT Owner.1173        /// 1174        /// # Arguments1175        /// 1176        /// * collection_id: ID of the collection.1177        /// 1178        /// * item_id: ID of NFT to burn.1179        #[weight = <T as Config>::WeightInfo::burn_item()]1180        #[transactional]1181        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11821183            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1184            let target_collection = Self::get_collection(collection_id)?;11851186            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;11871188            Ok(())1189        }11901191        /// Change ownership of the token.1192        /// 1193        /// # Permissions1194        /// 1195        /// * Collection Owner1196        /// * Collection Admin1197        /// * Current NFT owner1198        ///1199        /// # Arguments1200        /// 1201        /// * recipient: Address of token recipient.1202        /// 1203        /// * collection_id.1204        /// 1205        /// * item_id: ID of the item1206        ///     * Non-Fungible Mode: Required.1207        ///     * Fungible Mode: Ignored.1208        ///     * Re-Fungible Mode: Required.1209        /// 1210        /// * value: Amount to transfer.1211        ///     * Non-Fungible Mode: Ignored1212        ///     * Fungible Mode: Must specify transferred amount1213        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1214        #[weight = <T as Config>::WeightInfo::transfer()]1215        #[transactional]1216        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1217            let sender = ensure_signed(origin)?;1218            let collection = Self::get_collection(collection_id)?;12191220            Self::transfer_internal(sender, recipient, &collection, item_id, value)1221        }12221223        /// Set, change, or remove approved address to transfer the ownership of the NFT.1224        /// 1225        /// # Permissions1226        /// 1227        /// * Collection Owner1228        /// * Collection Admin1229        /// * Current NFT owner1230        /// 1231        /// # Arguments1232        /// 1233        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1234        /// 1235        /// * collection_id.1236        /// 1237        /// * item_id: ID of the item.1238        #[weight = <T as Config>::WeightInfo::approve()]1239        #[transactional]1240        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12411242            let sender = ensure_signed(origin)?;1243            let target_collection = Self::get_collection(collection_id)?;12441245            Self::token_exists(&target_collection, item_id)?;12461247            // Transfer permissions check1248            let bypasses_limits = target_collection.limits.owner_can_transfer &&1249                Self::is_owner_or_admin_permissions(1250                    &target_collection,1251                    sender.clone(),1252                );12531254            let allowance_limit = if bypasses_limits {1255                None1256            } else if let Some(amount) = Self::owned_amount(1257                sender.clone(),1258                &target_collection,1259                item_id,1260            ) {1261                Some(amount)1262            } else {1263                fail!(Error::<T>::NoPermission);1264            };12651266            if target_collection.access == AccessMode::WhiteList {1267                Self::check_white_list(&target_collection, &sender)?;1268                Self::check_white_list(&target_collection, &spender)?;1269            }12701271            let allowance: u128 = amount1272                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1273                .ok_or(Error::<T>::NumOverflow)?;1274            if let Some(limit) = allowance_limit {1275                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1276            }1277            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12781279            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1280            Ok(())1281        }1282        1283        /// 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.1284        /// 1285        /// # Permissions1286        /// * Collection Owner1287        /// * Collection Admin1288        /// * Current NFT owner1289        /// * Address approved by current NFT owner1290        /// 1291        /// # Arguments1292        /// 1293        /// * from: Address that owns token.1294        /// 1295        /// * recipient: Address of token recipient.1296        /// 1297        /// * collection_id.1298        /// 1299        /// * item_id: ID of the item.1300        /// 1301        /// * value: Amount to transfer.1302        #[weight = <T as Config>::WeightInfo::transfer_from()]1303        #[transactional]1304        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {13051306            let sender = ensure_signed(origin)?;1307            let target_collection = Self::get_collection(collection_id)?;13081309            // Check approval1310            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));13111312            // Limits check1313            Self::is_correct_transfer(&target_collection, &recipient)?;13141315            // Transfer permissions check         1316            ensure!(1317                approval >= value || 1318                (1319                    target_collection.limits.owner_can_transfer &&1320                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1321                ),1322                Error::<T>::NoPermission1323            );13241325            if target_collection.access == AccessMode::WhiteList {1326                Self::check_white_list(&target_collection, &sender)?;1327                Self::check_white_list(&target_collection, &recipient)?;1328            }13291330            // Reduce approval by transferred amount or remove if remaining approval drops to 01331            if approval.saturating_sub(value) > 0 {1332                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1333            }1334            else {1335                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1336            }13371338            match target_collection.mode1339            {1340                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1341                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1342                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1343                _ => ()1344            };13451346            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1347            Ok(())1348        }13491350        // #[weight = 0]1351        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13521353        //     // let no_perm_mes = "You do not have permissions to modify this collection";1354        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1355        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1356        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13571358        //     // // on_nft_received  call13591360        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13611362        //     Ok(())1363        // }13641365        /// Set off-chain data schema.1366        /// 1367        /// # Permissions1368        /// 1369        /// * Collection Owner1370        /// * Collection Admin1371        /// 1372        /// # Arguments1373        /// 1374        /// * collection_id.1375        /// 1376        /// * schema: String representing the offchain data schema.1377        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1378        #[transactional]1379        pub fn set_variable_meta_data (1380            origin,1381            collection_id: CollectionId,1382            item_id: TokenId,1383            data: Vec<u8>1384        ) -> DispatchResult {1385            let sender = ensure_signed(origin)?;1386            1387            let target_collection = Self::get_collection(collection_id)?;1388            Self::token_exists(&target_collection, item_id)?;13891390            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13911392            // Modify permissions check1393            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1394                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1395                Error::<T>::NoPermission);13961397            match target_collection.mode1398            {1399                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1400                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1401                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1402                _ => fail!(Error::<T>::UnexpectedCollectionType)1403            };14041405            Ok(())1406        }1407 1408        /// Set schema standard1409        /// ImageURL1410        /// Unique1411        /// 1412        /// # Permissions1413        /// 1414        /// * Collection Owner1415        /// * Collection Admin1416        /// 1417        /// # Arguments1418        /// 1419        /// * collection_id.1420        /// 1421        /// * schema: SchemaVersion: enum1422        #[weight = <T as Config>::WeightInfo::set_schema_version()]1423        #[transactional]1424        pub fn set_schema_version(1425            origin,1426            collection_id: CollectionId,1427            version: SchemaVersion1428        ) -> DispatchResult {1429            let sender = ensure_signed(origin)?;1430            let mut target_collection = Self::get_collection(collection_id)?;1431            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1432            target_collection.schema_version = version;1433            Self::save_collection(target_collection);14341435            Ok(())1436        }14371438        /// Set off-chain data schema.1439        /// 1440        /// # Permissions1441        /// 1442        /// * Collection Owner1443        /// * Collection Admin1444        /// 1445        /// # Arguments1446        /// 1447        /// * collection_id.1448        /// 1449        /// * schema: String representing the offchain data schema.1450        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1451        #[transactional]1452        pub fn set_offchain_schema(1453            origin,1454            collection_id: CollectionId,1455            schema: Vec<u8>1456        ) -> DispatchResult {1457            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1458            let mut target_collection = Self::get_collection(collection_id)?;1459            Self::check_owner_or_admin_permissions(&target_collection, sender)?;14601461            // check schema limit1462            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14631464            target_collection.offchain_schema = schema;1465            Self::save_collection(target_collection);14661467            Ok(())1468        }14691470        /// Set const on-chain data schema.1471        /// 1472        /// # Permissions1473        /// 1474        /// * Collection Owner1475        /// * Collection Admin1476        /// 1477        /// # Arguments1478        /// 1479        /// * collection_id.1480        /// 1481        /// * schema: String representing the const on-chain data schema.1482        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1483        #[transactional]1484        pub fn set_const_on_chain_schema (1485            origin,1486            collection_id: CollectionId,1487            schema: Vec<u8>1488        ) -> DispatchResult {1489            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1490            let mut target_collection = Self::get_collection(collection_id)?;1491            Self::check_owner_or_admin_permissions(&target_collection, sender)?;14921493            // check schema limit1494            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14951496            target_collection.const_on_chain_schema = schema;1497            Self::save_collection(target_collection);14981499            Ok(())1500        }15011502        /// Set variable on-chain data schema.1503        /// 1504        /// # Permissions1505        /// 1506        /// * Collection Owner1507        /// * Collection Admin1508        /// 1509        /// # Arguments1510        /// 1511        /// * collection_id.1512        /// 1513        /// * schema: String representing the variable on-chain data schema.1514        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1515        #[transactional]1516        pub fn set_variable_on_chain_schema (1517            origin,1518            collection_id: CollectionId,1519            schema: Vec<u8>1520        ) -> DispatchResult {1521            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1522            let mut target_collection = Self::get_collection(collection_id)?;1523            Self::check_owner_or_admin_permissions(&target_collection, sender)?;15241525            // check schema limit1526            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");15271528            target_collection.variable_on_chain_schema = schema;1529            Self::save_collection(target_collection);15301531            Ok(())1532        }15331534        // Sudo permissions function1535        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1536        #[transactional]1537        pub fn set_chain_limits(1538            origin,1539            limits: ChainLimits1540        ) -> DispatchResult {15411542            #[cfg(not(feature = "runtime-benchmarks"))]1543            ensure_root(origin)?;15441545            <ChainLimit>::put(limits);1546            Ok(())1547        }15481549        /// Enable smart contract self-sponsoring.1550        /// 1551        /// # Permissions1552        /// 1553        /// * Contract Owner1554        /// 1555        /// # Arguments1556        /// 1557        /// * contract address1558        /// * enable flag1559        /// 1560        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1561        #[transactional]1562        pub fn enable_contract_sponsoring(1563            origin,1564            contract_address: T::AccountId,1565            enable: bool1566        ) -> DispatchResult {15671568            let sender = ensure_signed(origin)?;15691570            #[cfg(feature = "runtime-benchmarks")]1571            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15721573            Self::ensure_contract_owned(sender, &contract_address)?;15741575            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1576            Ok(())1577        }15781579        /// Set the rate limit for contract sponsoring to specified number of blocks.1580        /// 1581        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1582        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1583        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1584        /// from contract endowment if there are at least B blocks between such transactions. 1585        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1586        /// 1587        /// # Permissions1588        /// 1589        /// * Contract Owner1590        /// 1591        /// # Arguments1592        /// 1593        /// -`contract_address`: Address of the contract to sponsor1594        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1595        /// 1596        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1597        #[transactional]1598        pub fn set_contract_sponsoring_rate_limit(1599            origin,1600            contract_address: T::AccountId,1601            rate_limit: T::BlockNumber1602        ) -> DispatchResult {1603            let sender = ensure_signed(origin)?;16041605            #[cfg(feature = "runtime-benchmarks")]1606            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16071608            Self::ensure_contract_owned(sender, &contract_address)?;1609            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1610            Ok(())1611        }16121613        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1614        /// 1615        /// # Permissions1616        /// 1617        /// * Address that deployed smart contract.1618        /// 1619        /// # Arguments1620        /// 1621        /// -`contract_address`: Address of the contract.1622        /// 1623        /// - `enable`: .  1624        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1625        #[transactional]1626        pub fn toggle_contract_white_list(1627            origin,1628            contract_address: T::AccountId,1629            enable: bool1630        ) -> DispatchResult {1631            let sender = ensure_signed(origin)?;16321633            #[cfg(feature = "runtime-benchmarks")]1634            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16351636            Self::ensure_contract_owned(sender, &contract_address)?;1637            if enable {1638                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1639            } else {1640                <ContractWhiteListEnabled<T>>::remove(contract_address);1641            }1642            Ok(())1643        }1644        1645        /// Add an address to smart contract white list.1646        /// 1647        /// # Permissions1648        /// 1649        /// * Address that deployed smart contract.1650        /// 1651        /// # Arguments1652        /// 1653        /// -`contract_address`: Address of the contract.1654        ///1655        /// -`account_address`: Address to add.1656        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1657        #[transactional]1658        pub fn add_to_contract_white_list(1659            origin,1660            contract_address: T::AccountId,1661            account_address: T::AccountId1662        ) -> DispatchResult {1663            let sender = ensure_signed(origin)?;16641665            #[cfg(feature = "runtime-benchmarks")]1666            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1667            1668            Self::ensure_contract_owned(sender, &contract_address)?;      1669            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1670            Ok(())1671        }16721673        /// Remove an address from smart contract white list.1674        /// 1675        /// # Permissions1676        /// 1677        /// * Address that deployed smart contract.1678        /// 1679        /// # Arguments1680        /// 1681        /// -`contract_address`: Address of the contract.1682        ///1683        /// -`account_address`: Address to remove.1684        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1685        #[transactional]1686        pub fn remove_from_contract_white_list(1687            origin,1688            contract_address: T::AccountId,1689            account_address: T::AccountId1690        ) -> DispatchResult {1691            let sender = ensure_signed(origin)?;16921693            #[cfg(feature = "runtime-benchmarks")]1694            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16951696            Self::ensure_contract_owned(sender, &contract_address)?;1697            <ContractWhiteList<T>>::remove(contract_address, account_address);1698            Ok(())1699        }17001701        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1702        #[transactional]1703        pub fn set_collection_limits(1704            origin,1705            collection_id: u32,1706            new_limits: CollectionLimits<T::BlockNumber>,1707        ) -> DispatchResult {1708            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1709            let mut target_collection = Self::get_collection(collection_id)?;1710            Self::check_owner_permissions(&target_collection, sender.clone())?;1711            let old_limits = &target_collection.limits;1712            let chain_limits = ChainLimit::get();17131714            // collection bounds1715            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1716                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1717                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1718                Error::<T>::CollectionLimitBoundsExceeded);17191720            // token_limit   check  prev1721            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1722            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);17231724            ensure!(1725                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1726                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1727                Error::<T>::OwnerPermissionsCantBeReverted,1728            );17291730            target_collection.limits = new_limits;1731            Self::save_collection(target_collection);17321733            Ok(())1734        } 1735    }1736}17371738impl<T: Config> Module<T> {17391740    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1741        // Limits check1742        Self::is_correct_transfer(target_collection, &recipient)?;17431744        // Transfer permissions check1745        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1746            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1747            Error::<T>::NoPermission);17481749        if target_collection.access == AccessMode::WhiteList {1750            Self::check_white_list(target_collection, &sender)?;1751            Self::check_white_list(target_collection, &recipient)?;1752        }17531754        match target_collection.mode1755        {1756            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1757            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1758            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1759            _ => ()1760        };17611762        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17631764        Ok(())1765    }17661767	pub fn approve_internal(1768		sender: T::AccountId,1769		spender: T::AccountId,1770		collection: &CollectionHandle<T>,1771		item_id: TokenId,1772		amount: u1281773	) -> DispatchResult {1774		Self::token_exists(&collection, item_id)?;17751776		// Transfer permissions check1777		let bypasses_limits = collection.limits.owner_can_transfer &&1778			Self::is_owner_or_admin_permissions(1779				&collection,1780				sender.clone(),1781			);17821783		let allowance_limit = if bypasses_limits {1784			None1785		} else if let Some(amount) = Self::owned_amount(1786			sender.clone(),1787			&collection,1788			item_id,1789		) {1790			Some(amount)1791		} else {1792			fail!(Error::<T>::NoPermission);1793		};17941795		if collection.access == AccessMode::WhiteList {1796			Self::check_white_list(&collection, &sender)?;1797			Self::check_white_list(&collection, &spender)?;1798		}17991800		let allowance: u128 = amount1801			.checked_add(<Allowances<T>>::get(collection.id, (item_id, &sender, &spender)))1802			.ok_or(Error::<T>::NumOverflow)?;1803		if let Some(limit) = allowance_limit {1804			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1805		}1806		<Allowances<T>>::insert(collection.id, (item_id, sender.clone(), spender.clone()), allowance);18071808		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1809		Ok(())1810	}18111812	pub fn transfer_from_internal(1813		sender: T::AccountId,1814		from: T::AccountId,1815		recipient: T::AccountId,1816		collection: &CollectionHandle<T>,1817		item_id: TokenId,1818		amount: u128,1819	) -> DispatchResult {1820		// Check approval1821		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, &from, &sender));18221823		// Limits check1824		Self::is_correct_transfer(&collection, &recipient)?;18251826		// Transfer permissions check1827		ensure!(1828			approval >= amount || 1829			(1830				collection.limits.owner_can_transfer &&1831				Self::is_owner_or_admin_permissions(&collection, sender.clone())1832			),1833			Error::<T>::NoPermission1834		);18351836		if collection.access == AccessMode::WhiteList {1837			Self::check_white_list(&collection, &sender)?;1838			Self::check_white_list(&collection, &recipient)?;1839		}18401841		// Reduce approval by transferred amount or remove if remaining approval drops to 01842		if approval.saturating_sub(amount) > 0 {1843			<Allowances<T>>::insert(collection.id, (item_id, &from, &sender), approval - amount);1844		} else {1845			<Allowances<T>>::remove(collection.id, (item_id, &from, &sender));1846		}18471848		match collection.mode {1849			CollectionMode::NFT => {1850				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1851			}1852			CollectionMode::Fungible(_) => {1853				Self::transfer_fungible(&collection, amount, &from, &recipient)?1854			}1855			CollectionMode::ReFungible => {1856				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1857			}1858			_ => ()1859		};18601861    pub fn create_multiple_items_internal(1862        sender: T::CrossAccountId,1863        collection: &CollectionHandle<T>,1864        owner: T::CrossAccountId,1865        items_data: Vec<CreateItemData>,1866    ) -> DispatchResult {1867        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18681869        for data in &items_data {1870            Self::validate_create_item_args(&collection, data)?;1871        }1872        for data in &items_data {1873            Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1874        }18751876        Ok(())1877    }18781879    pub fn burn_item_internal(1880        sender: &T::CrossAccountId,1881        collection: &CollectionHandle<T>,1882        item_id: TokenId,1883        value: u128,1884    ) -> DispatchResult {1885        ensure!(1886            Self::is_item_owner(sender.clone(), &collection, item_id) ||1887            (1888                collection.limits.owner_can_transfer &&1889                Self::is_owner_or_admin_permissions(&collection, sender.clone())1890            ),1891            Error::<T>::NoPermission1892        );18931894        if collection.access == AccessMode::WhiteList {1895            Self::check_white_list(&collection, &sender)?;1896        }18971898        match collection.mode1899        {1900            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1901            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1902            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1903            _ => ()1904        };19051906        Ok(())1907    }19081909    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::AccountId) -> DispatchResult {1910        let collection_id = collection.id;19111912        // check token limit and account token limit1913        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1914        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1915        1916        Ok(())1917    }19181919    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1920        let collection_id = collection.id;19211922        // check token limit and account token limit1923        let total_items: u32 = ItemListIndex::get(collection_id)1924            .checked_add(amount)1925            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1926        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1927            .checked_add(amount)1928            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1929        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1930        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19311932        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1933            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1934            Self::check_white_list(collection, owner)?;1935            Self::check_white_list(collection, sender)?;1936        }19371938        Ok(())1939    }19401941    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1942        match target_collection.mode1943        {1944            CollectionMode::NFT => {1945                if let CreateItemData::NFT(data) = data {1946                    // check sizes1947                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1948                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1949                } else {1950                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1951                }1952            },1953            CollectionMode::Fungible(_) => {1954                if let CreateItemData::Fungible(_) = data {1955                } else {1956                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1957                }1958            },1959            CollectionMode::ReFungible => {1960                if let CreateItemData::ReFungible(data) = data {19611962                    // check sizes1963                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1964                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19651966                    // Check refungibility limits1967                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1968                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1969                } else {1970                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1971                }1972            },1973            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1974        };19751976        Ok(())1977    }19781979    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1980        match data1981        {1982            CreateItemData::NFT(data) => {1983                let item = NftItemType {1984                    owner: owner.clone(),1985                    const_data: data.const_data,1986                    variable_data: data.variable_data1987                };19881989                Self::add_nft_item(collection, item)?;1990            },1991            CreateItemData::Fungible(data) => {1992                Self::add_fungible_item(collection, &owner, data.value)?;1993            },1994            CreateItemData::ReFungible(data) => {1995                let mut owner_list = Vec::new();1996                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});19971998                let item = ReFungibleItemType {1999                    owner: owner_list,2000                    const_data: data.const_data,2001                    variable_data: data.variable_data2002                };20032004                Self::add_refungible_item(collection, item)?;2005            }2006        };20072008        Ok(())2009    }20102011    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2012        let collection_id = collection.id;20132014        // Does new owner already have an account?2015        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20162017        // Mint 2018        let item = FungibleItemType {2019            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2020        };2021        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20222023        // Update balance2024        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2025            .checked_add(value)2026            .ok_or(Error::<T>::NumOverflow)?;2027        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20282029        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2030        Ok(())2031    }20322033    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2034        let collection_id = collection.id;20352036        let current_index = <ItemListIndex>::get(collection_id)2037            .checked_add(1)2038            .ok_or(Error::<T>::NumOverflow)?;2039        let itemcopy = item.clone();20402041        ensure!(2042            item.owner.len() == 1,2043            Error::<T>::BadCreateRefungibleCall,2044        );2045        let item_owner = item.owner.first().expect("only one owner is defined");20462047        let value = item_owner.fraction;2048        let owner = item_owner.owner.clone();20492050        Self::add_token_index(collection_id, current_index, &owner)?;20512052        <ItemListIndex>::insert(collection_id, current_index);2053        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20542055        // Update balance2056        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2057            .checked_add(value)2058            .ok_or(Error::<T>::NumOverflow)?;2059        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20602061        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2062        Ok(())2063    }20642065    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2066        let collection_id = collection.id;20672068        let current_index = <ItemListIndex>::get(collection_id)2069            .checked_add(1)2070            .ok_or(Error::<T>::NumOverflow)?;20712072        let item_owner = item.owner.clone();2073        Self::add_token_index(collection_id, current_index, &item.owner)?;20742075        <ItemListIndex>::insert(collection_id, current_index);2076        <NftItemList<T>>::insert(collection_id, current_index, item);20772078        // Update balance2079        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2080            .checked_add(1)2081            .ok_or(Error::<T>::NumOverflow)?;2082        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);20832084        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2085        Ok(())2086    }20872088    fn burn_refungible_item(2089        collection: &CollectionHandle<T>,2090        item_id: TokenId,2091        owner: &T::CrossAccountId,2092    ) -> DispatchResult {2093        let collection_id = collection.id;20942095        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2096            .ok_or(Error::<T>::TokenNotFound)?;2097        let rft_balance = token2098            .owner2099            .iter()2100            .find(|&i| i.owner == *owner)2101            .ok_or(Error::<T>::TokenNotFound)?;2102        Self::remove_token_index(collection_id, item_id, owner)?;21032104        // update balance2105        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2106            .checked_sub(rft_balance.fraction)2107            .ok_or(Error::<T>::NumOverflow)?;2108        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21092110        // Re-create owners list with sender removed2111        let index = token2112            .owner2113            .iter()2114            .position(|i| i.owner == *owner)2115            .expect("owned item is exists");2116        token.owner.remove(index);2117        let owner_count = token.owner.len();21182119        // Burn the token completely if this was the last (only) owner2120        if owner_count == 0 {2121            <ReFungibleItemList<T>>::remove(collection_id, item_id);2122            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2123        }2124        else {2125            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2126        }21272128        Ok(())2129    }21302131    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2132        let collection_id = collection.id;21332134        let item = <NftItemList<T>>::get(collection_id, item_id)2135            .ok_or(Error::<T>::TokenNotFound)?;2136        Self::remove_token_index(collection_id, item_id, &item.owner)?;21372138        // update balance2139        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2140            .checked_sub(1)2141            .ok_or(Error::<T>::NumOverflow)?;2142        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2143        <NftItemList<T>>::remove(collection_id, item_id);2144        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21452146        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2147        Ok(())2148    }21492150    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2151        let collection_id = collection.id;21522153        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2154        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21552156        // update balance2157        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2158            .checked_sub(value)2159            .ok_or(Error::<T>::NumOverflow)?;2160        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21612162        if balance.value - value > 0 {2163            balance.value -= value;2164            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2165        }2166        else {2167            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2168        }21692170        Ok(())2171    }21722173    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2174        Ok(<CollectionById<T>>::get(collection_id)2175            .map(|collection| CollectionHandle {2176                id: collection_id,2177                collection2178            })2179            .ok_or(Error::<T>::CollectionNotFound)?)2180    }21812182    fn save_collection(collection: CollectionHandle<T>) {2183        <CollectionById<T>>::insert(collection.id, collection.collection);2184    }21852186    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {2187        ensure!(2188            subject == target_collection.owner,2189            Error::<T>::NoPermission2190        );21912192        Ok(())2193    }21942195    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {2196        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2197    }21982199    fn check_owner_or_admin_permissions(2200        collection: &CollectionHandle<T>,2201        subject: T::AccountId,2202    ) -> DispatchResult {2203        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22042205        Ok(())2206    }22072208    fn owned_amount(2209        subject: T::AccountId,2210        target_collection: &CollectionHandle<T>,2211        item_id: TokenId,2212    ) -> Option<u128> {2213        let collection_id = target_collection.id;22142215        match target_collection.mode {2216            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)2217                .then(|| 1),2218            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)2219                .value),2220            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2221                .owner2222                .iter()2223                .find(|i| i.owner == subject)2224                .map(|i| i.fraction),2225            CollectionMode::Invalid => None,2226        }2227    }22282229    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2230        match target_collection.mode {2231            CollectionMode::Fungible(_) => true,2232            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),2233        }2234    }22352236    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {2237        let collection_id = collection.id;22382239        let mes = Error::<T>::AddresNotInWhiteList;2240        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);22412242        Ok(())2243    }22442245    /// Check if token exists. In case of Fungible, check if there is an entry for 2246    /// the owner in fungible balances double map2247    fn token_exists(2248        target_collection: &CollectionHandle<T>,2249        item_id: TokenId,2250    ) -> DispatchResult {2251        let collection_id = target_collection.id;2252        let exists = match target_collection.mode2253        {2254            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2255            CollectionMode::Fungible(_)  => true,2256            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2257            _ => false2258        };22592260        ensure!(exists == true, Error::<T>::TokenNotFound);2261        Ok(())2262    }22632264    fn transfer_fungible(2265        collection: &CollectionHandle<T>,2266        value: u128,2267        owner: &T::AccountId,2268        recipient: &T::AccountId,2269    ) -> DispatchResult {2270        let collection_id = collection.id;22712272        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2273        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);22742275        // Send balance to recipient (updates balanceOf of recipient)2276        Self::add_fungible_item(collection, recipient, value)?;22772278        // update balanceOf of sender2279        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);22802281        // Reduce or remove sender2282        if balance.value == value {2283            <FungibleItemList<T>>::remove(collection_id, owner);2284        }2285        else {2286            balance.value -= value;2287            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2288        }22892290        Ok(())2291    }22922293    fn transfer_refungible(2294        collection: &CollectionHandle<T>,2295        item_id: TokenId,2296        value: u128,2297        owner: T::AccountId,2298        new_owner: T::AccountId,2299    ) -> DispatchResult {2300        let collection_id = collection.id;2301        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2302            .ok_or(Error::<T>::TokenNotFound)?;23032304        let item = full_item2305            .owner2306            .iter()2307            .filter(|i| i.owner == owner)2308            .next()2309            .ok_or(Error::<T>::TokenNotFound)?;2310        let amount = item.fraction;23112312        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23132314        // update balance2315        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2316            .checked_sub(value)2317            .ok_or(Error::<T>::NumOverflow)?;2318        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);23192320        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.clone())2321            .checked_add(value)2322            .ok_or(Error::<T>::NumOverflow)?;2323        <Balance<T>>::insert(collection_id, new_owner.clone(), balance_new_owner);23242325        let old_owner = item.owner.clone();2326        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23272328        // transfer2329        if amount == value && !new_owner_has_account {2330            // change owner2331            // new owner do not have account2332            let mut new_full_item = full_item.clone();2333            new_full_item2334                .owner2335                .iter_mut()2336                .find(|i| i.owner == owner)2337                .expect("old owner does present in refungible")2338                .owner = new_owner.clone();2339            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23402341            // update index collection2342            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2343        } else {2344            let mut new_full_item = full_item.clone();2345            new_full_item2346                .owner2347                .iter_mut()2348                .find(|i| i.owner == owner)2349                .expect("old owner does present in refungible")2350                .fraction -= value;23512352            // separate amount2353            if new_owner_has_account {2354                // new owner has account2355                new_full_item2356                    .owner2357                    .iter_mut()2358                    .find(|i| i.owner == new_owner)2359                    .expect("new owner has account")2360                    .fraction += value;2361            } else {2362                // new owner do not have account2363                new_full_item.owner.push(Ownership {2364                    owner: new_owner.clone(),2365                    fraction: value,2366                });2367                Self::add_token_index(collection_id, item_id, &new_owner)?;2368            }23692370            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2371        }23722373        Ok(())2374    }23752376    fn transfer_nft(2377        collection: &CollectionHandle<T>,2378        item_id: TokenId,2379        sender: T::AccountId,2380        new_owner: T::AccountId,2381    ) -> DispatchResult {2382        let collection_id = collection.id;2383        let mut item = <NftItemList<T>>::get(collection_id, item_id)2384            .ok_or(Error::<T>::TokenNotFound)?;23852386        ensure!(2387            sender == item.owner,2388            Error::<T>::MustBeTokenOwner2389        );23902391        // update balance2392        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2393            .checked_sub(1)2394            .ok_or(Error::<T>::NumOverflow)?;2395        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);23962397        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2398            .checked_add(1)2399            .ok_or(Error::<T>::NumOverflow)?;2400        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);24012402        // change owner2403        let old_owner = item.owner.clone();2404        item.owner = new_owner.clone();2405        <NftItemList<T>>::insert(collection_id, item_id, item);24062407        // update index collection2408        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24092410        Ok(())2411    }2412    2413    fn set_re_fungible_variable_data(2414        collection: &CollectionHandle<T>,2415        item_id: TokenId,2416        data: Vec<u8>2417    ) -> DispatchResult {2418        let collection_id = collection.id;2419        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2420            .ok_or(Error::<T>::TokenNotFound)?;24212422        item.variable_data = data;24232424        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24252426        Ok(())2427    }24282429    fn set_nft_variable_data(2430        collection: &CollectionHandle<T>,2431        item_id: TokenId,2432        data: Vec<u8>2433    ) -> DispatchResult {2434        let collection_id = collection.id;2435        let mut item = <NftItemList<T>>::get(collection_id, item_id)2436            .ok_or(Error::<T>::TokenNotFound)?;2437        2438        item.variable_data = data;24392440        <NftItemList<T>>::insert(collection_id, item_id, item);2441        2442        Ok(())2443    }24442445    fn init_collection(item: &Collection<T>) {2446        // check params2447        assert!(2448            item.decimal_points <= MAX_DECIMAL_POINTS,2449            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2450        );2451        assert!(2452            item.name.len() <= 64,2453            "Collection name can not be longer than 63 char"2454        );2455        assert!(2456            item.name.len() <= 256,2457            "Collection description can not be longer than 255 char"2458        );2459        assert!(2460            item.token_prefix.len() <= 16,2461            "Token prefix can not be longer than 15 char"2462        );24632464        // Generate next collection ID2465        let next_id = CreatedCollectionCount::get()2466            .checked_add(1)2467            .unwrap();24682469        CreatedCollectionCount::put(next_id);2470    }24712472    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2473        let current_index = <ItemListIndex>::get(collection_id)2474            .checked_add(1)2475            .unwrap();24762477        let item_owner = item.owner.clone();2478        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();24792480        <ItemListIndex>::insert(collection_id, current_index);24812482        // Update balance2483        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2484            .checked_add(1)2485            .unwrap();2486        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2487    }24882489    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2490        let current_index = <ItemListIndex>::get(collection_id)2491            .checked_add(1)2492            .unwrap();24932494        Self::add_token_index(collection_id, current_index, owner).unwrap();24952496        <ItemListIndex>::insert(collection_id, current_index);24972498        // Update balance2499        let new_balance = <Balance<T>>::get(collection_id, owner)2500            .checked_add(item.value)2501            .unwrap();2502        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2503    }25042505    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2506        let current_index = <ItemListIndex>::get(collection_id)2507            .checked_add(1)2508            .unwrap();25092510        let value = item.owner.first().unwrap().fraction;2511        let owner = item.owner.first().unwrap().owner.clone();25122513        Self::add_token_index(collection_id, current_index, &owner).unwrap();25142515        <ItemListIndex>::insert(collection_id, current_index);25162517        // Update balance2518        let new_balance = <Balance<T>>::get(collection_id, &owner)2519            .checked_add(value)2520            .unwrap();2521        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2522    }25232524    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2525        // add to account limit2526        if <AccountItemCount<T>>::contains_key(owner) {25272528            // bound Owned tokens by a single address2529            let count = <AccountItemCount<T>>::get(owner);2530            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25312532            <AccountItemCount<T>>::insert(owner.clone(), count2533                .checked_add(1)2534                .ok_or(Error::<T>::NumOverflow)?);2535        }2536        else {2537            <AccountItemCount<T>>::insert(owner.clone(), 1);2538        }25392540        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2541        if list_exists {2542            let mut list = <AddressTokens<T>>::get(collection_id, owner);2543            let item_contains = list.contains(&item_index.clone());25442545            if !item_contains {2546                list.push(item_index.clone());2547            }25482549            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2550        } else {2551            let mut itm = Vec::new();2552            itm.push(item_index.clone());2553            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2554        }25552556        Ok(())2557    }25582559    fn remove_token_index(2560        collection_id: CollectionId,2561        item_index: TokenId,2562        owner: &T::AccountId,2563    ) -> DispatchResult {25642565        // update counter2566        <AccountItemCount<T>>::insert(owner.clone(), 2567            <AccountItemCount<T>>::get(owner)2568            .checked_sub(1)2569            .ok_or(Error::<T>::NumOverflow)?);257025712572        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2573        if list_exists {2574            let mut list = <AddressTokens<T>>::get(collection_id, owner);2575            let item_contains = list.contains(&item_index.clone());25762577            if item_contains {2578                list.retain(|&item| item != item_index);2579                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2580            }2581        }25822583        Ok(())2584    }25852586    fn move_token_index(2587        collection_id: CollectionId,2588        item_index: TokenId,2589        old_owner: &T::AccountId,2590        new_owner: &T::AccountId,2591    ) -> DispatchResult {2592        Self::remove_token_index(collection_id, item_index, old_owner)?;2593        Self::add_token_index(collection_id, item_index, new_owner)?;25942595        Ok(())2596    }2597    2598    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2599        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26002601        Ok(())2602    }2603}26042605////////////////////////////////////////////////////////////////////////////////////////////////////2606// Economic models2607// #region26082609/// Fee multiplier.2610pub type Multiplier = FixedU128;26112612type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26132614/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2615/// in the queue.2616#[derive(Encode, Decode, Clone, Eq, PartialEq)]2617pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26182619impl<T: Config + Send + Sync> sp_std::fmt::Debug 2620    for ChargeTransactionPayment<T>2621{2622	#[cfg(feature = "std")]2623	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2624		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2625	}2626	#[cfg(not(feature = "std"))]2627	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2628		Ok(())2629	}2630}26312632impl<T: Config> ChargeTransactionPayment<T>2633where2634    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2635    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2636    T::AccountId: AsRef<[u8]>,2637    T::AccountId: UncheckedFrom<T::Hash>,2638{2639    fn traditional_fee(2640        len: usize,2641        info: &DispatchInfoOf<T::Call>,2642        tip: BalanceOf<T>,2643    ) -> BalanceOf<T>2644    where2645        T::Call: Dispatchable<Info = DispatchInfo>,2646    {2647        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2648    }26492650	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2651        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2652        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2653        let len_saturation = max_block_length as u64 / (len as u64).max(1);2654        let coefficient: BalanceOf<T> = weight_saturation2655            .min(len_saturation)2656            .saturated_into::<BalanceOf<T>>();2657        final_fee2658            .saturating_mul(coefficient)2659            .saturated_into::<TransactionPriority>()2660    }26612662    fn withdraw_fee(2663        &self,2664        who: &T::AccountId,2665        call: &T::Call,2666        info: &DispatchInfoOf<T::Call>,2667        len: usize,2668	) -> Result<2669		(2670			BalanceOf<T>,2671			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2672		),2673		TransactionValidityError,2674	> {2675        let tip = self.0;26762677        let fee = Self::traditional_fee(len, info, tip);26782679        // Only mess with balances if fee is not zero.2680        if fee.is_zero() {2681            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2682			.map(|i| (fee, i));2683        }26842685        // Determine who is paying transaction fee based on ecnomic model2686        // Parse call to extract collection ID and access collection sponsor2687        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2688            Some(Call::create_item(collection_id, _owner, _properties)) => {2689                let collection = <CollectionById<T>>::get(collection_id)?;26902691                // sponsor timeout2692                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26932694                let limit = collection.limits.sponsor_transfer_timeout;2695                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2696                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2697                    let limit_time = last_tx_block + limit.into();2698                    if block_number <= limit_time {2699                        return None;2700                    }2701                }2702                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27032704                // check free create limit2705                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2706                    collection.sponsorship.sponsor()2707                        .cloned()2708                } else {2709                    None2710                }2711            }2712            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2713                let collection = <CollectionById<T>>::get(collection_id)?;2714                2715                let mut sponsor_transfer = false;2716                if collection.sponsorship.confirmed() {27172718                    let collection_limits = collection.limits;2719                    let collection_mode = collection.mode;2720    2721                    // sponsor timeout2722                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2723                    sponsor_transfer = match collection_mode {2724                        CollectionMode::NFT => {2725    2726                            // get correct limit2727                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2728                                collection_limits.sponsor_transfer_timeout2729                            } else {2730                                ChainLimit::get().nft_sponsor_transfer_timeout2731                            };2732    2733                            let mut sponsored = true;2734                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2735                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2736                                let limit_time = last_tx_block + limit.into();2737                                if block_number <= limit_time {2738                                    sponsored = false;2739                                }2740                            }2741                            if sponsored {2742                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2743                            }27442745                            sponsored2746                        }2747                        CollectionMode::Fungible(_) => {2748    2749                            // get correct limit2750                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2751                                collection_limits.sponsor_transfer_timeout2752                            } else {2753                                ChainLimit::get().fungible_sponsor_transfer_timeout2754                            };2755    2756                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2757                            let mut sponsored = true;2758                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2759                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2760                                let limit_time = last_tx_block + limit.into();2761                                if block_number <= limit_time {2762                                    sponsored = false;2763                                }2764                            }2765                            if sponsored {2766                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2767                            }27682769                            sponsored2770                        }2771                        CollectionMode::ReFungible => {2772    2773                            // get correct limit2774                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2775                                collection_limits.sponsor_transfer_timeout2776                            } else {2777                                ChainLimit::get().refungible_sponsor_transfer_timeout2778                            };2779    2780                            let mut sponsored = true;2781                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2782                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2783                                let limit_time = last_tx_block + limit.into();2784                                if block_number <= limit_time {2785                                    sponsored = false;2786                                }2787                            }2788                            if sponsored {2789                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2790                            }27912792                            sponsored2793                        }2794                        _ => {2795                            false2796                        },2797                    };2798                }27992800                if !sponsor_transfer {2801                    None2802                } else {2803                    collection.sponsorship.sponsor()2804                        .cloned()2805                }2806            }28072808            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2809                let mut sponsor_metadata_changes = false;28102811                let collection = <CollectionById<T>>::get(collection_id)?;28122813                if2814                    collection.sponsorship.confirmed() &&2815                    // Can't sponsor fungible collection, this tx will be rejected2816                    // as invalid2817                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2818                    data.len() <= collection.limits.sponsored_data_size as usize2819                {2820                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2821                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28222823                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2824                            .map(|last_block| block_number - last_block > rate_limit)2825                            .unwrap_or(true) 2826                        {2827                            sponsor_metadata_changes = true;2828                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2829                        }2830                    }2831                }28322833                if !sponsor_metadata_changes {2834                    None2835                } else {2836                    collection.sponsorship.sponsor().cloned()2837                }2838            }28392840            _ => None,2841        })();28422843        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2844            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28452846                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28472848                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2849                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2850                  2851                if !owned_contract && white_list_enabled {2852                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2853                        return Err(InvalidTransaction::Call.into());2854                    }2855                }2856            },2857            _ => {},2858        }28592860        // Sponsor smart contracts2861        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {28622863            // On instantiation: set the contract owner2864            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {28652866                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2867                    &who,2868                    code_hash,2869                    salt,2870                );2871                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28722873                None2874            },28752876            // On instantiation with code: set the contract owner2877            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {28782879                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2880                    &who,2881                    &T::Hashing::hash(&_code),2882                    _salt,2883                );28842885                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28862887                None2888            }28892890            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2891            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28922893                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28942895                let mut sponsor_transfer = false;2896                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2897                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2898                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2899                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2900                    let limit_time = last_tx_block + rate_limit;29012902                    if block_number >= limit_time {2903                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2904                        sponsor_transfer = true;2905                    }2906                } else {2907                    sponsor_transfer = false;2908                }2909               2910                if sponsor_transfer {2911                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2912                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2913                            return Some(called_contract);2914                        }2915                    }2916                }29172918                None2919            },29202921            _ => None,2922        });29232924        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29252926		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2927			.map(|i| (fee, i))2928    }2929}293029312932impl<T: Config + Send + Sync> SignedExtension2933    for ChargeTransactionPayment<T>2934where2935    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2936    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2937    T::AccountId: AsRef<[u8]>,2938    T::AccountId: UncheckedFrom<T::Hash>,2939{2940    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2941    type AccountId = T::AccountId;2942    type Call = T::Call;2943    type AdditionalSigned = ();2944    type Pre = (2945        // tip2946        BalanceOf<T>,2947        // who pays fee2948        Self::AccountId,2949		// imbalance resulting from withdrawing the fee2950		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2951    );2952    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2953        Ok(())2954    }29552956    fn validate(2957        &self,2958        who: &Self::AccountId,2959        call: &Self::Call,2960        info: &DispatchInfoOf<Self::Call>,2961        len: usize,2962    ) -> TransactionValidity {2963		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2964		Ok(ValidTransaction {2965			priority: Self::get_priority(len, info, fee),2966			..Default::default()2967		})2968    }29692970    fn pre_dispatch(2971        self,2972        who: &Self::AccountId,2973        call: &Self::Call,2974        info: &DispatchInfoOf<Self::Call>,2975        len: usize,2976    ) -> Result<Self::Pre, TransactionValidityError> {2977        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2978        Ok((self.0, who.clone(), imbalance))2979    }29802981    fn post_dispatch(2982        pre: Self::Pre,2983        info: &DispatchInfoOf<Self::Call>,2984        post_info: &PostDispatchInfoOf<Self::Call>,2985        len: usize,2986        _result: &DispatchResult,2987    ) -> Result<(), TransactionValidityError> {2988		let (tip, who, imbalance) = pre;2989		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2990			len as u32,2991			info,2992			post_info,2993			tip,2994		);2995		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2996		Ok(())2997    }2998}29993000// #endregion30013002sp_api::decl_runtime_apis! {3003    pub trait NftApi {3004        /// Used for ethereum integration3005        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3006    }3007}
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16    construct_runtime, decl_event, decl_module, decl_storage, decl_error,17    dispatch::DispatchResult,18    ensure, fail, parameter_types,19    traits::{20        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21        Randomness, IsSubType, WithdrawReasons,22    },23    weights::{24        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26        WeightToFeePolynomial, DispatchClass,27    },28    StorageValue,29    transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_evm::AddressMapping;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::account::*;5960pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;61pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;62pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;63pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6465// Structs66// #region6768pub type CollectionId = u32;69pub type TokenId = u32;70pub type DecimalPoints = u8;7172#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]73#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]74pub enum CollectionMode {75    Invalid,76    NFT,77    // decimal points78    Fungible(DecimalPoints),79    ReFungible,80}8182impl Default for CollectionMode {83    fn default() -> Self {84        Self::Invalid85    }86}8788impl Into<u8> for CollectionMode {89    fn into(self) -> u8 {90        match self {91            CollectionMode::Invalid => 0,92            CollectionMode::NFT => 1,93            CollectionMode::Fungible(_) => 2,94            CollectionMode::ReFungible => 3,95        }96    }97}9899#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]100#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]101pub enum AccessMode {102    Normal,103    WhiteList,104}105impl Default for AccessMode {106    fn default() -> Self {107        Self::Normal108    }109}110111#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]112#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]113pub enum SchemaVersion {114    ImageURL,115    Unique,116}117impl Default for SchemaVersion {118    fn default() -> Self {119        Self::ImageURL120    }121}122123#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]124#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]125pub struct Ownership<AccountId> {126    pub owner: AccountId,127    pub fraction: u128,128}129130#[derive(Encode, Decode, Debug, Clone, PartialEq)]131#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]132pub enum SponsorshipState<AccountId> {133    /// The fees are applied to the transaction sender134    Disabled,135    Unconfirmed(AccountId),136    /// Transactions are sponsored by specified account137    Confirmed(AccountId),138}139140impl<AccountId> SponsorshipState<AccountId> {141    fn sponsor(&self) -> Option<&AccountId> {142        match self {143            Self::Confirmed(sponsor) => Some(sponsor),144            _ => None,145        }146    }147148    fn pending_sponsor(&self) -> Option<&AccountId> {149        match self {150            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),151            _ => None,152        }153    }154155    fn confirmed(&self) -> bool {156        matches!(self, Self::Confirmed(_))157    }158}159160impl<T> Default for SponsorshipState<T> {161    fn default() -> Self {162        Self::Disabled163    }164}165166#[derive(Encode, Decode, Clone, PartialEq)]167#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]168pub struct Collection<T: Config> {169    pub owner: T::CrossAccountId,170    pub mode: CollectionMode,171    pub access: AccessMode,172    pub decimal_points: DecimalPoints,173    pub name: Vec<u16>,        // 64 include null escape char174    pub description: Vec<u16>, // 256 include null escape char175    pub token_prefix: Vec<u8>, // 16 include null escape char176    pub mint_mode: bool,177    pub offchain_schema: Vec<u8>,178    pub schema_version: SchemaVersion,179    pub sponsorship: SponsorshipState<T::AccountId>,180    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 181    pub variable_on_chain_schema: Vec<u8>, //182    pub const_on_chain_schema: Vec<u8>, //183}184185pub struct CollectionHandle<T: Config> {186    pub id: CollectionId,187    collection: Collection<T>,188}189190impl<T: Config> Deref for CollectionHandle<T> {191    type Target = Collection<T>;192193    fn deref(&self) -> &Self::Target {194        &self.collection195    }196}197198impl<T: Config> DerefMut for CollectionHandle<T> {199    fn deref_mut(&mut self) -> &mut Self::Target {200        &mut self.collection201    }202}203204#[derive(Encode, Decode, Debug, Clone, PartialEq)]205#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206pub struct NftItemType<AccountId> {207    pub owner: AccountId,208    pub const_data: Vec<u8>,209    pub variable_data: Vec<u8>,210}211212#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]213#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]214pub struct FungibleItemType {215    pub value: u128,216}217218#[derive(Encode, Decode, Debug, Clone, PartialEq)]219#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]220pub struct ReFungibleItemType<AccountId> {221    pub owner: Vec<Ownership<AccountId>>,222    pub const_data: Vec<u8>,223    pub variable_data: Vec<u8>,224}225226// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]227// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]228// pub struct VestingItem<AccountId, Moment> {229//     pub sender: AccountId,230//     pub recipient: AccountId,231//     pub collection_id: CollectionId,232//     pub item_id: TokenId,233//     pub amount: u64,234//     pub vesting_date: Moment,235// }236237#[derive(Encode, Decode, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct CollectionLimits<BlockNumber: Encode + Decode> {240    pub account_token_ownership_limit: u32,241    pub sponsored_data_size: u32,242    /// None - setVariableMetadata is not sponsored243    /// Some(v) - setVariableMetadata is sponsored 244    ///           if there is v block between txs245    pub sponsored_data_rate_limit: Option<BlockNumber>,246    pub token_limit: u32,247248    // Timeouts for item types in passed blocks249    pub sponsor_transfer_timeout: u32,250    pub owner_can_transfer: bool,251    pub owner_can_destroy: bool,252}253254impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {255    fn default() -> Self {256        Self { 257            account_token_ownership_limit: 10_000_000, 258            token_limit: u32::max_value(),259            sponsored_data_size: u32::MAX, 260            sponsored_data_rate_limit: None,261            sponsor_transfer_timeout: 14400,262            owner_can_transfer: true,263            owner_can_destroy: true264        }265    }266}267268#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]269#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]270pub struct ChainLimits {271    pub collection_numbers_limit: u32,272    pub account_token_ownership_limit: u32,273    pub collections_admins_limit: u64,274    pub custom_data_limit: u32,275276    // Timeouts for item types in passed blocks277    pub nft_sponsor_transfer_timeout: u32,278    pub fungible_sponsor_transfer_timeout: u32,279    pub refungible_sponsor_transfer_timeout: u32,280281    // Schema limits282    pub offchain_schema_limit: u32,283    pub variable_on_chain_schema_limit: u32,284    pub const_on_chain_schema_limit: u32,285}286287pub trait WeightInfo {288	fn create_collection() -> Weight;289	fn destroy_collection() -> Weight;290	fn add_to_white_list() -> Weight;291	fn remove_from_white_list() -> Weight;292    fn set_public_access_mode() -> Weight;293    fn set_mint_permission() -> Weight;294    fn change_collection_owner() -> Weight;295    fn add_collection_admin() -> Weight;296    fn remove_collection_admin() -> Weight;297    fn set_collection_sponsor() -> Weight;298    fn confirm_sponsorship() -> Weight;299    fn remove_collection_sponsor() -> Weight;300    fn create_item(s: usize) -> Weight;301    fn burn_item() -> Weight;302    fn transfer() -> Weight;303    fn approve() -> Weight;304    fn transfer_from() -> Weight;305    fn set_offchain_schema() -> Weight;306    fn set_const_on_chain_schema() -> Weight;307    fn set_variable_on_chain_schema() -> Weight;308    fn set_variable_meta_data() -> Weight;309    fn enable_contract_sponsoring() -> Weight;310    fn set_schema_version() -> Weight;311    fn set_chain_limits() -> Weight;312    fn set_contract_sponsoring_rate_limit() -> Weight;313    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;314    fn toggle_contract_white_list() -> Weight;315    fn add_to_contract_white_list() -> Weight;316    fn remove_from_contract_white_list() -> Weight;317    fn set_collection_limits() -> Weight;318}319320#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]321#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]322pub struct CreateNftData {323    pub const_data: Vec<u8>,324    pub variable_data: Vec<u8>,325}326327#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]328#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]329pub struct CreateFungibleData {330    pub value: u128,331}332333#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]334#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]335pub struct CreateReFungibleData {336    pub const_data: Vec<u8>,337    pub variable_data: Vec<u8>,338    pub pieces: u128,339}340341#[derive(Encode, Decode, Debug, Clone, PartialEq)]342#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]343pub enum CreateItemData {344    NFT(CreateNftData),345    Fungible(CreateFungibleData),346    ReFungible(CreateReFungibleData),347}348349impl CreateItemData {350    pub fn len(&self) -> usize {351        let len = match self {352            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),353            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),354            _ => 0355        };356        357        return len;358    }359}360361impl From<CreateNftData> for CreateItemData {362    fn from(item: CreateNftData) -> Self {363        CreateItemData::NFT(item)364    }365}366367impl From<CreateReFungibleData> for CreateItemData {368    fn from(item: CreateReFungibleData) -> Self {369        CreateItemData::ReFungible(item)370    }371}372373impl From<CreateFungibleData> for CreateItemData {374    fn from(item: CreateFungibleData) -> Self {375        CreateItemData::Fungible(item)376    }377}378379380decl_error! {381	/// Error for non-fungible-token module.382	pub enum Error for Module<T: Config> {383        /// Total collections bound exceeded.384        TotalCollectionsLimitExceeded,385		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.386        CollectionDecimalPointLimitExceeded, 387        /// Collection name can not be longer than 63 char.388        CollectionNameLimitExceeded, 389        /// Collection description can not be longer than 255 char.390        CollectionDescriptionLimitExceeded, 391        /// Token prefix can not be longer than 15 char.392        CollectionTokenPrefixLimitExceeded,393        /// This collection does not exist.394        CollectionNotFound,395        /// Item not exists.396        TokenNotFound,397        /// Admin not found398        AdminNotFound,399        /// Arithmetic calculation overflow.400        NumOverflow,       401        /// Account already has admin role.402        AlreadyAdmin,  403        /// You do not own this collection.404        NoPermission,405        /// This address is not set as sponsor, use setCollectionSponsor first.406        ConfirmUnsetSponsorFail,407        /// Collection is not in mint mode.408        PublicMintingNotAllowed,409        /// Sender parameter and item owner must be equal.410        MustBeTokenOwner,411        /// Item balance not enough.412        TokenValueTooLow,413        /// Size of item is too large.414        NftSizeLimitExceeded,415        /// No approve found416        ApproveNotFound,417        /// Requested value more than approved.418        TokenValueNotEnough,419        /// Only approved addresses can call this method.420        ApproveRequired,421        /// Address is not in white list.422        AddresNotInWhiteList,423        /// Number of collection admins bound exceeded.424        CollectionAdminsLimitExceeded,425        /// Owned tokens by a single address bound exceeded.426        AddressOwnershipLimitExceeded,427        /// Length of items properties must be greater than 0.428        EmptyArgument,429        /// const_data exceeded data limit.430        TokenConstDataLimitExceeded,431        /// variable_data exceeded data limit.432        TokenVariableDataLimitExceeded,433        /// Not NFT item data used to mint in NFT collection.434        NotNftDataUsedToMintNftCollectionToken,435        /// Not Fungible item data used to mint in Fungible collection.436        NotFungibleDataUsedToMintFungibleCollectionToken,437        /// Not Re Fungible item data used to mint in Re Fungible collection.438        NotReFungibleDataUsedToMintReFungibleCollectionToken,439        /// Unexpected collection type.440        UnexpectedCollectionType,441        /// Can't store metadata in fungible tokens.442        CantStoreMetadataInFungibleTokens,443        /// Collection token limit exceeded444        CollectionTokenLimitExceeded,445        /// Account token limit exceeded per collection446        AccountTokenLimitExceeded,447        /// Collection limit bounds per collection exceeded448        CollectionLimitBoundsExceeded,449        /// Tried to enable permissions which are only permitted to be disabled450        OwnerPermissionsCantBeReverted,451        /// Schema data size limit bound exceeded452        SchemaDataLimitExceeded,453        /// Maximum refungibility exceeded454        WrongRefungiblePieces,455        /// createRefungible should be called with one owner456        BadCreateRefungibleCall,457	}458}459460pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {461    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;462463    /// Weight information for extrinsics in this pallet.464	type WeightInfo: WeightInfo;465466    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;467    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;468    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;469470	type CrossAccountId: CrossAccountId<Self::AccountId>;471    type Currency: Currency<Self::AccountId>;472    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;473    type TreasuryAccountId: Get<Self::AccountId>;474}475476#[cfg(feature = "runtime-benchmarks")]477mod benchmarking;478479// #endregion480481// # Used definitions482//483// ## User control levels484//485// chain-controlled - key is uncontrolled by user486//                    i.e autoincrementing index487//                    can use non-cryptographic hash488// real - key is controlled by user489//        but it is hard to generate enough colliding values, i.e owner of signed txs490//        can use non-cryptographic hash491// controlled - key is completly controlled by users492//              i.e maps with mutable keys493//              should use cryptographic hash494//495// ## User control level downgrade reasons496//497// ?1 - chain-controlled -> controlled498//      collections/tokens can be destroyed, resulting in massive holes499// ?2 - chain-controlled -> controlled500//      same as ?1, but can be only added, resulting in easier exploitation501// ?3 - real -> controlled502//      no confirmation required, so addresses can be easily generated503decl_storage! {504    trait Store for Module<T: Config> as Nft {505506        //#region Private members507        /// Id of next collection508        CreatedCollectionCount: u32;509        /// Used for migrations510        ChainVersion: u64;511        /// Id of last collection token512        /// Collection id (controlled?1)513        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;514        //#endregion515516        //#region Chain limits struct517        pub ChainLimit get(fn chain_limit) config(): ChainLimits;518        //#endregion519520        //#region Bound counters521        /// Amount of collections destroyed, used for total amount tracking with522        /// CreatedCollectionCount523        DestroyedCollectionCount: u32;524        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)525        /// Account id (real)526        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;527        //#endregion528529        //#region Basic collections530        /// Collection info531        /// Collection id (controlled?1)532        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;533        /// List of collection admins534        /// Collection id (controlled?2)535        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;536        /// Whitelisted collection users537        /// Collection id (controlled?2), user id (controlled?3)538        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;539        //#endregion540541        /// How many of collection items user have542        /// Collection id (controlled?2), account id (real)543        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;544545        /// Amount of items which spender can transfer out of owners account (via transferFrom)546        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))547        /// TODO: Off chain worker should remove from this map when token gets removed548        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;549550        //#region Item collections551        /// Collection id (controlled?2), token id (controlled?1)552        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;553        /// Collection id (controlled?2), owner (controlled?2)554        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;555        /// Collection id (controlled?2), token id (controlled?1)556        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;557        //#endregion558559        //#region Index list560        /// Collection id (controlled?2), tokens owner (controlled?2)561        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;562        //#endregion563564        //#region Tokens transfer rate limit baskets565        /// (Collection id (controlled?2), who created (real))566        /// TODO: Off chain worker should remove from this map when collection gets removed567        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;568        /// Collection id (controlled?2), token id (controlled?2)569        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;570        /// Collection id (controlled?2), owning user (real)571        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;572        /// Collection id (controlled?2), token id (controlled?2)573        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;574        //#endregion575576        /// Variable metadata sponsoring577        /// Collection id (controlled?2), token id (controlled?2)578        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;579      580        //#region Contract Sponsorship and Ownership581        /// Contract address (real)582        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;583        /// Contract address (real)584        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;585        /// (Contract address(real), caller (real))586        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;587        /// Contract address (real)588        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;589        /// Contract address (real)590        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 591        /// Contract address (real) => Whitelisted user (controlled?3)592        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 593        //#endregion594    }595    add_extra_genesis {596        build(|config: &GenesisConfig<T>| {597            // Modification of storage598            for (_num, _c) in &config.collection_id {599                <Module<T>>::init_collection(_c);600            }601602            for (_num, _c, _i) in &config.nft_item_id {603                <Module<T>>::init_nft_token(*_c, _i);604            }605606            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {607                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);608            }609610            for (_num, _c, _i) in &config.refungible_item_id {611                <Module<T>>::init_refungible_token(*_c, _i);612            }613        })614    }615}616617decl_event!(618    pub enum Event<T>619    where620        CrossAccountId = <T as Config>::CrossAccountId,621    {622        /// New collection was created623        /// 624        /// # Arguments625        /// 626        /// * collection_id: Globally unique identifier of newly created collection.627        /// 628        /// * mode: [CollectionMode] converted into u8.629        /// 630        /// * account_id: Collection owner.631        CollectionCreated(CollectionId, u8, CrossAccountId),632633        /// New item was created.634        /// 635        /// # Arguments636        /// 637        /// * collection_id: Id of the collection where item was created.638        /// 639        /// * item_id: Id of an item. Unique within the collection.640        ///641        /// * recipient: Owner of newly created item 642        ItemCreated(CollectionId, TokenId, CrossAccountId),643644        /// Collection item was burned.645        /// 646        /// # Arguments647        /// 648        /// collection_id.649        /// 650        /// item_id: Identifier of burned NFT.651        ItemDestroyed(CollectionId, TokenId),652653        /// Item was transferred654        ///655        /// * collection_id: Id of collection to which item is belong656        ///657        /// * item_id: Id of an item658        ///659        /// * sender: Original owner of item660        ///661        /// * recipient: New owner of item662        ///663        /// * amount: Always 1 for NFT664        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),665666        /// * collection_id667        ///668        /// * item_id669        ///670        /// * sender671        ///672        /// * spender673        ///674        /// * amount675        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),676    }677);678679decl_module! {680    pub struct Module<T: Config> for enum Call 681    where 682        origin: T::Origin683    {684        fn deposit_event() = default;685        type Error = Error<T>;686687        fn on_initialize(now: T::BlockNumber) -> Weight {688            0689        }690691        /// 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.692        /// 693        /// # Permissions694        /// 695        /// * Anyone.696        /// 697        /// # Arguments698        /// 699        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.700        /// 701        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.702        /// 703        /// * token_prefix: UTF-8 string with token prefix.704        /// 705        /// * mode: [CollectionMode] collection type and type dependent data.706        // returns collection ID707        #[weight = <T as Config>::WeightInfo::create_collection()]708        #[transactional]709        pub fn create_collection(origin,710                                 collection_name: Vec<u16>,711                                 collection_description: Vec<u16>,712                                 token_prefix: Vec<u8>,713                                 mode: CollectionMode) -> DispatchResult {714715            // Anyone can create a collection716            let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);717718            // Take a (non-refundable) deposit of collection creation719            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();720            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(721                &T::TreasuryAccountId::get(),722                T::CollectionCreationPrice::get(),723            ));724            <T as Config>::Currency::settle(725                who.as_sub(),726                imbalance,727                WithdrawReasons::TRANSFER,728                ExistenceRequirement::KeepAlive,729            ).map_err(|_| Error::<T>::NoPermission)?;730731            let decimal_points = match mode {732                CollectionMode::Fungible(points) => points,733                _ => 0734            };735736            let chain_limit = ChainLimit::get();737738            let created_count = CreatedCollectionCount::get();739            let destroyed_count = DestroyedCollectionCount::get();740741            // bound Total number of collections742            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);743744            // check params745            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);746            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);747            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);748            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);749750            // Generate next collection ID751            let next_id = created_count752                .checked_add(1)753                .ok_or(Error::<T>::NumOverflow)?;754755            CreatedCollectionCount::put(next_id);756757            let limits = CollectionLimits {758                sponsored_data_size: chain_limit.custom_data_limit,759                ..Default::default()760            };761762            // Create new collection763            let new_collection = Collection {764                owner: who.clone(),765                name: collection_name,766                mode: mode.clone(),767                mint_mode: false,768                access: AccessMode::Normal,769                description: collection_description,770                decimal_points: decimal_points,771                token_prefix: token_prefix,772                offchain_schema: Vec::new(),773                schema_version: SchemaVersion::ImageURL,774                sponsorship: SponsorshipState::Disabled,775                variable_on_chain_schema: Vec::new(),776                const_on_chain_schema: Vec::new(),777                limits,778            };779780            // Add new collection to map781            <CollectionById<T>>::insert(next_id, new_collection);782783            // call event784            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));785786            Ok(())787        }788789        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.790        /// 791        /// # Permissions792        /// 793        /// * Collection Owner.794        /// 795        /// # Arguments796        /// 797        /// * collection_id: collection to destroy.798        #[weight = <T as Config>::WeightInfo::destroy_collection()]799        #[transactional]800        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {801802            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);803            let collection = Self::get_collection(collection_id)?;804            Self::check_owner_permissions(&collection, sender)?;805            if !collection.limits.owner_can_destroy {806                fail!(Error::<T>::NoPermission);807            }808809            <AddressTokens<T>>::remove_prefix(collection_id);810            <Allowances<T>>::remove_prefix(collection_id);811            <Balance<T>>::remove_prefix(collection_id);812            <ItemListIndex>::remove(collection_id);813            <AdminList<T>>::remove(collection_id);814            <CollectionById<T>>::remove(collection_id);815            <WhiteList<T>>::remove_prefix(collection_id);816817            <NftItemList<T>>::remove_prefix(collection_id);818            <FungibleItemList<T>>::remove_prefix(collection_id);819            <ReFungibleItemList<T>>::remove_prefix(collection_id);820821            <NftTransferBasket<T>>::remove_prefix(collection_id);822            <FungibleTransferBasket<T>>::remove_prefix(collection_id);823            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);824825            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);826827            DestroyedCollectionCount::put(DestroyedCollectionCount::get()828                .checked_add(1)829                .ok_or(Error::<T>::NumOverflow)?);830831            Ok(())832        }833834        /// Add an address to white list.835        /// 836        /// # Permissions837        /// 838        /// * Collection Owner839        /// * Collection Admin840        /// 841        /// # Arguments842        /// 843        /// * collection_id.844        /// 845        /// * address.846        #[weight = <T as Config>::WeightInfo::add_to_white_list()]847        #[transactional]848        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{849850            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);851            let collection = Self::get_collection(collection_id)?;852            Self::check_owner_or_admin_permissions(&collection, sender)?;853854            <WhiteList<T>>::insert(collection_id, address.as_sub(), true);855            856            Ok(())857        }858859        /// Remove an address from white list.860        /// 861        /// # Permissions862        /// 863        /// * Collection Owner864        /// * Collection Admin865        /// 866        /// # Arguments867        /// 868        /// * collection_id.869        /// 870        /// * address.871        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]872        #[transactional]873        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{874875            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);876            let collection = Self::get_collection(collection_id)?;877            Self::check_owner_or_admin_permissions(&collection, sender)?;878879            <WhiteList<T>>::remove(collection_id, address.as_sub());880881            Ok(())882        }883884        /// Toggle between normal and white list access for the methods with access for `Anyone`.885        /// 886        /// # Permissions887        /// 888        /// * Collection Owner.889        /// 890        /// # Arguments891        /// 892        /// * collection_id.893        /// 894        /// * mode: [AccessMode]895        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]896        #[transactional]897        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult898        {899            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);900901            let mut target_collection = Self::get_collection(collection_id)?;902            Self::check_owner_permissions(&target_collection, sender)?;903            target_collection.access = mode;904            Self::save_collection(target_collection);905906            Ok(())907        }908909        /// Allows Anyone to create tokens if:910        /// * White List is enabled, and911        /// * Address is added to white list, and912        /// * This method was called with True parameter913        /// 914        /// # Permissions915        /// * Collection Owner916        ///917        /// # Arguments918        /// 919        /// * collection_id.920        /// 921        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.922        #[weight = <T as Config>::WeightInfo::set_mint_permission()]923        #[transactional]924        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult925        {926            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);927928            let mut target_collection = Self::get_collection(collection_id)?;929            Self::check_owner_permissions(&target_collection, sender)?;930            target_collection.mint_mode = mint_permission;931            Self::save_collection(target_collection);932933            Ok(())934        }935936        /// Change the owner of the collection.937        /// 938        /// # Permissions939        /// 940        /// * Collection Owner.941        /// 942        /// # Arguments943        /// 944        /// * collection_id.945        /// 946        /// * new_owner.947        #[weight = <T as Config>::WeightInfo::change_collection_owner()]948        #[transactional]949        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {950951            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);952            let mut target_collection = Self::get_collection(collection_id)?;953            Self::check_owner_permissions(&target_collection, sender)?;954            target_collection.owner = new_owner;955            Self::save_collection(target_collection);956957            Ok(())958        }959960        /// Adds an admin of the Collection.961        /// 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. 962        /// 963        /// # Permissions964        /// 965        /// * Collection Owner.966        /// * Collection Admin.967        /// 968        /// # Arguments969        /// 970        /// * collection_id: ID of the Collection to add admin for.971        /// 972        /// * new_admin_id: Address of new admin to add.973        #[weight = <T as Config>::WeightInfo::add_collection_admin()]974        #[transactional]975        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {976977            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);978            let collection = Self::get_collection(collection_id)?;979            Self::check_owner_or_admin_permissions(&collection, sender)?;980            let mut admin_arr = <AdminList<T>>::get(collection_id);981982            match admin_arr.binary_search(&new_admin_id) {983                Ok(_) => {},984                Err(idx) => {985                    let limits = ChainLimit::get();986                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);987                    admin_arr.insert(idx, new_admin_id);988                    <AdminList<T>>::insert(collection_id, admin_arr);989                }990            }991            Ok(())992        }993994        /// 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.995        ///996        /// # Permissions997        /// 998        /// * Collection Owner.999        /// * Collection Admin.1000        /// 1001        /// # Arguments1002        /// 1003        /// * collection_id: ID of the Collection to remove admin for.1004        /// 1005        /// * account_id: Address of admin to remove.1006        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1007        #[transactional]1008        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {10091010            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1011            let collection = Self::get_collection(collection_id)?;1012            Self::check_owner_or_admin_permissions(&collection, sender)?;1013            let mut admin_arr = <AdminList<T>>::get(collection_id);10141015            match admin_arr.binary_search(&account_id) {1016                Ok(idx) => {1017                    admin_arr.remove(idx);1018                    <AdminList<T>>::insert(collection_id, admin_arr);1019                },1020                Err(_) => {}1021            }1022            Ok(())1023        }10241025        /// # Permissions1026        /// 1027        /// * Collection Owner1028        /// 1029        /// # Arguments1030        /// 1031        /// * collection_id.1032        /// 1033        /// * new_sponsor.1034        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1035        #[transactional]1036        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1037            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038            let mut target_collection = Self::get_collection(collection_id)?;1039            Self::check_owner_permissions(&target_collection, &sender)?;10401041            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1042            Self::save_collection(target_collection);10431044            Ok(())1045        }10461047        /// # Permissions1048        /// 1049        /// * Sponsor.1050        /// 1051        /// # Arguments1052        /// 1053        /// * collection_id.1054        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1055        #[transactional]1056        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1057            let sender = ensure_signed(origin)?;10581059            let mut target_collection = Self::get_collection(collection_id)?;1060            ensure!(1061                target_collection.sponsorship.pending_sponsor() == Some(&sender),1062                Error::<T>::ConfirmUnsetSponsorFail1063            );10641065            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1066            Self::save_collection(target_collection);10671068            Ok(())1069        }10701071        /// Switch back to pay-per-own-transaction model.1072        ///1073        /// # Permissions1074        ///1075        /// * Collection owner.1076        /// 1077        /// # Arguments1078        /// 1079        /// * collection_id.1080        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1081        #[transactional]1082        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1083            let sender = ensure_signed(origin)?;10841085            let mut target_collection = Self::get_collection(collection_id)?;1086            Self::check_owner_permissions(&target_collection, sender)?;10871088            target_collection.sponsorship = SponsorshipState::Disabled;1089            Self::save_collection(target_collection);10901091            Ok(())1092        }10931094        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1095        /// 1096        /// # Permissions1097        /// 1098        /// * Collection Owner.1099        /// * Collection Admin.1100        /// * Anyone if1101        ///     * White List is enabled, and1102        ///     * Address is added to white list, and1103        ///     * MintPermission is enabled (see SetMintPermission method)1104        /// 1105        /// # Arguments1106        /// 1107        /// * collection_id: ID of the collection.1108        /// 1109        /// * owner: Address, initial owner of the NFT.1110        ///1111        /// * data: Token data to store on chain.1112        // #[weight =1113        // (130_000_000 as Weight)1114        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1115        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1116        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11171118        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1119        #[transactional]1120        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {11211122            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);11231124            let target_collection = Self::get_collection(collection_id)?;11251126            Self::can_create_items_in_collection(&target_collection, &sender, &owner, 1)?;1127            Self::validate_create_item_args(&target_collection, &data)?;1128            Self::create_item_no_validation(&target_collection, owner, data)?;11291130            Ok(())1131        }11321133        /// This method creates multiple items in a collection created with CreateCollection method.1134        /// 1135        /// # Permissions1136        /// 1137        /// * Collection Owner.1138        /// * Collection Admin.1139        /// * Anyone if1140        ///     * White List is enabled, and1141        ///     * Address is added to white list, and1142        ///     * MintPermission is enabled (see SetMintPermission method)1143        /// 1144        /// # Arguments1145        /// 1146        /// * collection_id: ID of the collection.1147        /// 1148        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1149        /// 1150        /// * owner: Address, initial owner of the NFT.1151        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1152                               .map(|data| { data.len() })1153                               .sum())]1154        #[transactional]1155        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11561157            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1158            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1159            let collection = Self::get_collection(collection_id)?;11601161            Self::create_multiple_items_internal(sender, &collection, owner, items_data)?;11621163            Ok(())1164        }11651166        /// Destroys a concrete instance of NFT.1167        /// 1168        /// # Permissions1169        /// 1170        /// * Collection Owner.1171        /// * Collection Admin.1172        /// * Current NFT Owner.1173        /// 1174        /// # Arguments1175        /// 1176        /// * collection_id: ID of the collection.1177        /// 1178        /// * item_id: ID of NFT to burn.1179        #[weight = <T as Config>::WeightInfo::burn_item()]1180        #[transactional]1181        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11821183            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1184            let target_collection = Self::get_collection(collection_id)?;11851186            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;11871188            Ok(())1189        }11901191        /// Change ownership of the token.1192        /// 1193        /// # Permissions1194        /// 1195        /// * Collection Owner1196        /// * Collection Admin1197        /// * Current NFT owner1198        ///1199        /// # Arguments1200        /// 1201        /// * recipient: Address of token recipient.1202        /// 1203        /// * collection_id.1204        /// 1205        /// * item_id: ID of the item1206        ///     * Non-Fungible Mode: Required.1207        ///     * Fungible Mode: Ignored.1208        ///     * Re-Fungible Mode: Required.1209        /// 1210        /// * value: Amount to transfer.1211        ///     * Non-Fungible Mode: Ignored1212        ///     * Fungible Mode: Must specify transferred amount1213        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1214        #[weight = <T as Config>::WeightInfo::transfer()]1215        #[transactional]1216        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1217            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1218            let collection = Self::get_collection(collection_id)?;12191220            Self::transfer_internal(sender, recipient, &collection, item_id, value)?;12211222            Ok(())1223        }12241225        /// Set, change, or remove approved address to transfer the ownership of the NFT.1226        /// 1227        /// # Permissions1228        /// 1229        /// * Collection Owner1230        /// * Collection Admin1231        /// * Current NFT owner1232        /// 1233        /// # Arguments1234        /// 1235        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1236        /// 1237        /// * collection_id.1238        /// 1239        /// * item_id: ID of the item.1240        #[weight = <T as Config>::WeightInfo::approve()]1241        #[transactional]1242        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12431244            let sender = ensure_signed(origin)?;1245            let target_collection = Self::get_collection(collection_id)?;12461247            Self::token_exists(&target_collection, item_id)?;12481249            // Transfer permissions check1250            let bypasses_limits = target_collection.limits.owner_can_transfer &&1251                Self::is_owner_or_admin_permissions(1252                    &target_collection,1253                    sender.clone(),1254                );12551256            let allowance_limit = if bypasses_limits {1257                None1258            } else if let Some(amount) = Self::owned_amount(1259                sender.clone(),1260                &target_collection,1261                item_id,1262            ) {1263                Some(amount)1264            } else {1265                fail!(Error::<T>::NoPermission);1266            };12671268            if target_collection.access == AccessMode::WhiteList {1269                Self::check_white_list(&target_collection, &sender)?;1270                Self::check_white_list(&target_collection, &spender)?;1271            }12721273            let allowance: u128 = amount1274                .checked_add(<Allowances<T>>::get(collection_id, (item_id, &sender, &spender)))1275                .ok_or(Error::<T>::NumOverflow)?;1276            if let Some(limit) = allowance_limit {1277                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1278            }1279            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12801281            Self::deposit_event(RawEvent::Approved(target_collection.id, item_id, sender, spender, allowance));1282            Ok(())1283        }1284        1285        /// 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.1286        /// 1287        /// # Permissions1288        /// * Collection Owner1289        /// * Collection Admin1290        /// * Current NFT owner1291        /// * Address approved by current NFT owner1292        /// 1293        /// # Arguments1294        /// 1295        /// * from: Address that owns token.1296        /// 1297        /// * recipient: Address of token recipient.1298        /// 1299        /// * collection_id.1300        /// 1301        /// * item_id: ID of the item.1302        /// 1303        /// * value: Amount to transfer.1304        #[weight = <T as Config>::WeightInfo::transfer_from()]1305        #[transactional]1306        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {13071308            let sender = ensure_signed(origin)?;1309            let target_collection = Self::get_collection(collection_id)?;13101311            // Check approval1312            let approval: u128 = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));13131314            // Limits check1315            Self::is_correct_transfer(&target_collection, &recipient)?;13161317            // Transfer permissions check         1318            ensure!(1319                approval >= value || 1320                (1321                    target_collection.limits.owner_can_transfer &&1322                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1323                ),1324                Error::<T>::NoPermission1325            );13261327            if target_collection.access == AccessMode::WhiteList {1328                Self::check_white_list(&target_collection, &sender)?;1329                Self::check_white_list(&target_collection, &recipient)?;1330            }13311332            // Reduce approval by transferred amount or remove if remaining approval drops to 01333            if approval.saturating_sub(value) > 0 {1334                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1335            }1336            else {1337                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1338            }13391340            match target_collection.mode1341            {1342                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from.clone(), recipient.clone())?,1343                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1344                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient.clone())?,1345                _ => ()1346            };13471348            Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, from, recipient, value));1349            Ok(())1350        }13511352        // #[weight = 0]1353        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13541355        //     // let no_perm_mes = "You do not have permissions to modify this collection";1356        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1357        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1358        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13591360        //     // // on_nft_received  call13611362        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13631364        //     Ok(())1365        // }13661367        /// Set off-chain data schema.1368        /// 1369        /// # Permissions1370        /// 1371        /// * Collection Owner1372        /// * Collection Admin1373        /// 1374        /// # Arguments1375        /// 1376        /// * collection_id.1377        /// 1378        /// * schema: String representing the offchain data schema.1379        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1380        #[transactional]1381        pub fn set_variable_meta_data (1382            origin,1383            collection_id: CollectionId,1384            item_id: TokenId,1385            data: Vec<u8>1386        ) -> DispatchResult {1387            let sender = ensure_signed(origin)?;1388            1389            let target_collection = Self::get_collection(collection_id)?;1390            Self::token_exists(&target_collection, item_id)?;13911392            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13931394            // Modify permissions check1395            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1396                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1397                Error::<T>::NoPermission);13981399            match target_collection.mode1400            {1401                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1402                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, item_id, data)?,1403                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1404                _ => fail!(Error::<T>::UnexpectedCollectionType)1405            };14061407            Ok(())1408        }1409 1410        /// Set schema standard1411        /// ImageURL1412        /// Unique1413        /// 1414        /// # Permissions1415        /// 1416        /// * Collection Owner1417        /// * Collection Admin1418        /// 1419        /// # Arguments1420        /// 1421        /// * collection_id.1422        /// 1423        /// * schema: SchemaVersion: enum1424        #[weight = <T as Config>::WeightInfo::set_schema_version()]1425        #[transactional]1426        pub fn set_schema_version(1427            origin,1428            collection_id: CollectionId,1429            version: SchemaVersion1430        ) -> DispatchResult {1431            let sender = ensure_signed(origin)?;1432            let mut target_collection = Self::get_collection(collection_id)?;1433            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1434            target_collection.schema_version = version;1435            Self::save_collection(target_collection);14361437            Ok(())1438        }14391440        /// Set off-chain data schema.1441        /// 1442        /// # Permissions1443        /// 1444        /// * Collection Owner1445        /// * Collection Admin1446        /// 1447        /// # Arguments1448        /// 1449        /// * collection_id.1450        /// 1451        /// * schema: String representing the offchain data schema.1452        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1453        #[transactional]1454        pub fn set_offchain_schema(1455            origin,1456            collection_id: CollectionId,1457            schema: Vec<u8>1458        ) -> DispatchResult {1459            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1460            let mut target_collection = Self::get_collection(collection_id)?;1461            Self::check_owner_or_admin_permissions(&target_collection, sender)?;14621463            // check schema limit1464            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14651466            target_collection.offchain_schema = schema;1467            Self::save_collection(target_collection);14681469            Ok(())1470        }14711472        /// Set const on-chain data schema.1473        /// 1474        /// # Permissions1475        /// 1476        /// * Collection Owner1477        /// * Collection Admin1478        /// 1479        /// # Arguments1480        /// 1481        /// * collection_id.1482        /// 1483        /// * schema: String representing the const on-chain data schema.1484        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1485        #[transactional]1486        pub fn set_const_on_chain_schema (1487            origin,1488            collection_id: CollectionId,1489            schema: Vec<u8>1490        ) -> DispatchResult {1491            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1492            let mut target_collection = Self::get_collection(collection_id)?;1493            Self::check_owner_or_admin_permissions(&target_collection, sender)?;14941495            // check schema limit1496            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14971498            target_collection.const_on_chain_schema = schema;1499            Self::save_collection(target_collection);15001501            Ok(())1502        }15031504        /// Set variable on-chain data schema.1505        /// 1506        /// # Permissions1507        /// 1508        /// * Collection Owner1509        /// * Collection Admin1510        /// 1511        /// # Arguments1512        /// 1513        /// * collection_id.1514        /// 1515        /// * schema: String representing the variable on-chain data schema.1516        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1517        #[transactional]1518        pub fn set_variable_on_chain_schema (1519            origin,1520            collection_id: CollectionId,1521            schema: Vec<u8>1522        ) -> DispatchResult {1523            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1524            let mut target_collection = Self::get_collection(collection_id)?;1525            Self::check_owner_or_admin_permissions(&target_collection, sender)?;15261527            // check schema limit1528            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");15291530            target_collection.variable_on_chain_schema = schema;1531            Self::save_collection(target_collection);15321533            Ok(())1534        }15351536        // Sudo permissions function1537        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1538        #[transactional]1539        pub fn set_chain_limits(1540            origin,1541            limits: ChainLimits1542        ) -> DispatchResult {15431544            #[cfg(not(feature = "runtime-benchmarks"))]1545            ensure_root(origin)?;15461547            <ChainLimit>::put(limits);1548            Ok(())1549        }15501551        /// Enable smart contract self-sponsoring.1552        /// 1553        /// # Permissions1554        /// 1555        /// * Contract Owner1556        /// 1557        /// # Arguments1558        /// 1559        /// * contract address1560        /// * enable flag1561        /// 1562        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1563        #[transactional]1564        pub fn enable_contract_sponsoring(1565            origin,1566            contract_address: T::AccountId,1567            enable: bool1568        ) -> DispatchResult {15691570            let sender = ensure_signed(origin)?;15711572            #[cfg(feature = "runtime-benchmarks")]1573            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15741575            Self::ensure_contract_owned(sender, &contract_address)?;15761577            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1578            Ok(())1579        }15801581        /// Set the rate limit for contract sponsoring to specified number of blocks.1582        /// 1583        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1584        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1585        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1586        /// from contract endowment if there are at least B blocks between such transactions. 1587        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1588        /// 1589        /// # Permissions1590        /// 1591        /// * Contract Owner1592        /// 1593        /// # Arguments1594        /// 1595        /// -`contract_address`: Address of the contract to sponsor1596        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1597        /// 1598        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1599        #[transactional]1600        pub fn set_contract_sponsoring_rate_limit(1601            origin,1602            contract_address: T::AccountId,1603            rate_limit: T::BlockNumber1604        ) -> DispatchResult {1605            let sender = ensure_signed(origin)?;16061607            #[cfg(feature = "runtime-benchmarks")]1608            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16091610            Self::ensure_contract_owned(sender, &contract_address)?;1611            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1612            Ok(())1613        }16141615        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1616        /// 1617        /// # Permissions1618        /// 1619        /// * Address that deployed smart contract.1620        /// 1621        /// # Arguments1622        /// 1623        /// -`contract_address`: Address of the contract.1624        /// 1625        /// - `enable`: .  1626        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1627        #[transactional]1628        pub fn toggle_contract_white_list(1629            origin,1630            contract_address: T::AccountId,1631            enable: bool1632        ) -> DispatchResult {1633            let sender = ensure_signed(origin)?;16341635            #[cfg(feature = "runtime-benchmarks")]1636            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16371638            Self::ensure_contract_owned(sender, &contract_address)?;1639            if enable {1640                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1641            } else {1642                <ContractWhiteListEnabled<T>>::remove(contract_address);1643            }1644            Ok(())1645        }1646        1647        /// Add an address to smart contract white list.1648        /// 1649        /// # Permissions1650        /// 1651        /// * Address that deployed smart contract.1652        /// 1653        /// # Arguments1654        /// 1655        /// -`contract_address`: Address of the contract.1656        ///1657        /// -`account_address`: Address to add.1658        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1659        #[transactional]1660        pub fn add_to_contract_white_list(1661            origin,1662            contract_address: T::AccountId,1663            account_address: T::AccountId1664        ) -> DispatchResult {1665            let sender = ensure_signed(origin)?;16661667            #[cfg(feature = "runtime-benchmarks")]1668            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1669            1670            Self::ensure_contract_owned(sender, &contract_address)?;      1671            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1672            Ok(())1673        }16741675        /// Remove an address from smart contract white list.1676        /// 1677        /// # Permissions1678        /// 1679        /// * Address that deployed smart contract.1680        /// 1681        /// # Arguments1682        /// 1683        /// -`contract_address`: Address of the contract.1684        ///1685        /// -`account_address`: Address to remove.1686        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1687        #[transactional]1688        pub fn remove_from_contract_white_list(1689            origin,1690            contract_address: T::AccountId,1691            account_address: T::AccountId1692        ) -> DispatchResult {1693            let sender = ensure_signed(origin)?;16941695            #[cfg(feature = "runtime-benchmarks")]1696            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16971698            Self::ensure_contract_owned(sender, &contract_address)?;1699            <ContractWhiteList<T>>::remove(contract_address, account_address);1700            Ok(())1701        }17021703        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1704        #[transactional]1705        pub fn set_collection_limits(1706            origin,1707            collection_id: u32,1708            new_limits: CollectionLimits<T::BlockNumber>,1709        ) -> DispatchResult {1710            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1711            let mut target_collection = Self::get_collection(collection_id)?;1712            Self::check_owner_permissions(&target_collection, sender.clone())?;1713            let old_limits = &target_collection.limits;1714            let chain_limits = ChainLimit::get();17151716            // collection bounds1717            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1718                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1719                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1720                Error::<T>::CollectionLimitBoundsExceeded);17211722            // token_limit   check  prev1723            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1724            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);17251726            ensure!(1727                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1728                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1729                Error::<T>::OwnerPermissionsCantBeReverted,1730            );17311732            target_collection.limits = new_limits;1733            Self::save_collection(target_collection);17341735            Ok(())1736        } 1737    }1738}17391740impl<T: Config> Module<T> {17411742    pub fn transfer_internal(sender: T::CrossAccountId, recipient: T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1743        // Limits check1744        Self::is_correct_transfer(target_collection, &recipient)?;17451746        // Transfer permissions check1747        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1748            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1749            Error::<T>::NoPermission);17501751        if target_collection.access == AccessMode::WhiteList {1752            Self::check_white_list(target_collection, &sender)?;1753            Self::check_white_list(target_collection, &recipient)?;1754        }17551756        match target_collection.mode1757        {1758            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1759            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1760            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1761            _ => ()1762        };17631764        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));17651766        Ok(())1767    }17681769	pub fn approve_internal(1770		sender: T::AccountId,1771		spender: T::AccountId,1772		collection: &CollectionHandle<T>,1773		item_id: TokenId,1774		amount: u1281775	) -> DispatchResult {1776		Self::token_exists(&collection, item_id)?;17771778		// Transfer permissions check1779		let bypasses_limits = collection.limits.owner_can_transfer &&1780			Self::is_owner_or_admin_permissions(1781				&collection,1782				sender.clone(),1783			);17841785		let allowance_limit = if bypasses_limits {1786			None1787		} else if let Some(amount) = Self::owned_amount(1788			sender.clone(),1789			&collection,1790			item_id,1791		) {1792			Some(amount)1793		} else {1794			fail!(Error::<T>::NoPermission);1795		};17961797		if collection.access == AccessMode::WhiteList {1798			Self::check_white_list(&collection, &sender)?;1799			Self::check_white_list(&collection, &spender)?;1800		}18011802		let allowance: u128 = amount1803			.checked_add(<Allowances<T>>::get(collection.id, (item_id, &sender, &spender)))1804			.ok_or(Error::<T>::NumOverflow)?;1805		if let Some(limit) = allowance_limit {1806			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1807		}1808		<Allowances<T>>::insert(collection.id, (item_id, sender.clone(), spender.clone()), allowance);18091810		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender, spender, allowance));1811		Ok(())1812	}18131814	pub fn transfer_from_internal(1815		sender: T::AccountId,1816		from: T::AccountId,1817		recipient: T::AccountId,1818		collection: &CollectionHandle<T>,1819		item_id: TokenId,1820		amount: u128,1821	) -> DispatchResult {1822		// Check approval1823		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, &from, &sender));18241825		// Limits check1826		Self::is_correct_transfer(&collection, &recipient)?;18271828		// Transfer permissions check1829		ensure!(1830			approval >= amount || 1831			(1832				collection.limits.owner_can_transfer &&1833				Self::is_owner_or_admin_permissions(&collection, sender.clone())1834			),1835			Error::<T>::NoPermission1836		);18371838		if collection.access == AccessMode::WhiteList {1839			Self::check_white_list(&collection, &sender)?;1840			Self::check_white_list(&collection, &recipient)?;1841		}18421843		// Reduce approval by transferred amount or remove if remaining approval drops to 01844		if approval.saturating_sub(amount) > 0 {1845			<Allowances<T>>::insert(collection.id, (item_id, &from, &sender), approval - amount);1846		} else {1847			<Allowances<T>>::remove(collection.id, (item_id, &from, &sender));1848		}18491850		match collection.mode {1851			CollectionMode::NFT => {1852				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1853			}1854			CollectionMode::Fungible(_) => {1855				Self::transfer_fungible(&collection, amount, &from, &recipient)?1856			}1857			CollectionMode::ReFungible => {1858				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1859			}1860			_ => ()1861		};18621863    pub fn create_multiple_items_internal(1864        sender: T::CrossAccountId,1865        collection: &CollectionHandle<T>,1866        owner: T::CrossAccountId,1867        items_data: Vec<CreateItemData>,1868    ) -> DispatchResult {1869        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18701871        for data in &items_data {1872            Self::validate_create_item_args(&collection, data)?;1873        }1874        for data in &items_data {1875            Self::create_item_no_validation(&collection, owner.clone(), data.clone())?;1876        }18771878        Ok(())1879    }18801881    pub fn burn_item_internal(1882        sender: &T::CrossAccountId,1883        collection: &CollectionHandle<T>,1884        item_id: TokenId,1885        value: u128,1886    ) -> DispatchResult {1887        ensure!(1888            Self::is_item_owner(sender.clone(), &collection, item_id) ||1889            (1890                collection.limits.owner_can_transfer &&1891                Self::is_owner_or_admin_permissions(&collection, sender.clone())1892            ),1893            Error::<T>::NoPermission1894        );18951896        if collection.access == AccessMode::WhiteList {1897            Self::check_white_list(&collection, &sender)?;1898        }18991900        match collection.mode1901        {1902            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1903            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1904            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1905            _ => ()1906        };19071908        Ok(())1909    }19101911    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1912        let collection_id = collection.id;19131914        // check token limit and account token limit1915        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1916        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1917        1918        Ok(())1919    }19201921    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::AccountId, owner: &T::AccountId, amount: u32) -> DispatchResult {1922        let collection_id = collection.id;19231924        // check token limit and account token limit1925        let total_items: u32 = ItemListIndex::get(collection_id)1926            .checked_add(amount)1927            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1928        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner).len() as u32)1929            .checked_add(amount)1930            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1931        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1932        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19331934        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1935            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1936            Self::check_white_list(collection, owner)?;1937            Self::check_white_list(collection, sender)?;1938        }19391940        Ok(())1941    }19421943    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1944        match target_collection.mode1945        {1946            CollectionMode::NFT => {1947                if let CreateItemData::NFT(data) = data {1948                    // check sizes1949                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1950                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1951                } else {1952                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1953                }1954            },1955            CollectionMode::Fungible(_) => {1956                if let CreateItemData::Fungible(_) = data {1957                } else {1958                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1959                }1960            },1961            CollectionMode::ReFungible => {1962                if let CreateItemData::ReFungible(data) = data {19631964                    // check sizes1965                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1966                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19671968                    // Check refungibility limits1969                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1970                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1971                } else {1972                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1973                }1974            },1975            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1976        };19771978        Ok(())1979    }19801981    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1982        match data1983        {1984            CreateItemData::NFT(data) => {1985                let item = NftItemType {1986                    owner: owner.clone(),1987                    const_data: data.const_data,1988                    variable_data: data.variable_data1989                };19901991                Self::add_nft_item(collection, item)?;1992            },1993            CreateItemData::Fungible(data) => {1994                Self::add_fungible_item(collection, &owner, data.value)?;1995            },1996            CreateItemData::ReFungible(data) => {1997                let mut owner_list = Vec::new();1998                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});19992000                let item = ReFungibleItemType {2001                    owner: owner_list,2002                    const_data: data.const_data,2003                    variable_data: data.variable_data2004                };20052006                Self::add_refungible_item(collection, item)?;2007            }2008        };20092010        Ok(())2011    }20122013    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2014        let collection_id = collection.id;20152016        // Does new owner already have an account?2017        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20182019        // Mint 2020        let item = FungibleItemType {2021            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2022        };2023        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20242025        // Update balance2026        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2027            .checked_add(value)2028            .ok_or(Error::<T>::NumOverflow)?;2029        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20302031        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2032        Ok(())2033    }20342035    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2036        let collection_id = collection.id;20372038        let current_index = <ItemListIndex>::get(collection_id)2039            .checked_add(1)2040            .ok_or(Error::<T>::NumOverflow)?;2041        let itemcopy = item.clone();20422043        ensure!(2044            item.owner.len() == 1,2045            Error::<T>::BadCreateRefungibleCall,2046        );2047        let item_owner = item.owner.first().expect("only one owner is defined");20482049        let value = item_owner.fraction;2050        let owner = item_owner.owner.clone();20512052        Self::add_token_index(collection_id, current_index, &owner)?;20532054        <ItemListIndex>::insert(collection_id, current_index);2055        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20562057        // Update balance2058        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2059            .checked_add(value)2060            .ok_or(Error::<T>::NumOverflow)?;2061        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20622063        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2064        Ok(())2065    }20662067    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2068        let collection_id = collection.id;20692070        let current_index = <ItemListIndex>::get(collection_id)2071            .checked_add(1)2072            .ok_or(Error::<T>::NumOverflow)?;20732074        let item_owner = item.owner.clone();2075        Self::add_token_index(collection_id, current_index, &item.owner)?;20762077        <ItemListIndex>::insert(collection_id, current_index);2078        <NftItemList<T>>::insert(collection_id, current_index, item);20792080        // Update balance2081        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2082            .checked_add(1)2083            .ok_or(Error::<T>::NumOverflow)?;2084        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);20852086        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2087        Ok(())2088    }20892090    fn burn_refungible_item(2091        collection: &CollectionHandle<T>,2092        item_id: TokenId,2093        owner: &T::CrossAccountId,2094    ) -> DispatchResult {2095        let collection_id = collection.id;20962097        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2098            .ok_or(Error::<T>::TokenNotFound)?;2099        let rft_balance = token2100            .owner2101            .iter()2102            .find(|&i| i.owner == *owner)2103            .ok_or(Error::<T>::TokenNotFound)?;2104        Self::remove_token_index(collection_id, item_id, owner)?;21052106        // update balance2107        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2108            .checked_sub(rft_balance.fraction)2109            .ok_or(Error::<T>::NumOverflow)?;2110        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21112112        // Re-create owners list with sender removed2113        let index = token2114            .owner2115            .iter()2116            .position(|i| i.owner == *owner)2117            .expect("owned item is exists");2118        token.owner.remove(index);2119        let owner_count = token.owner.len();21202121        // Burn the token completely if this was the last (only) owner2122        if owner_count == 0 {2123            <ReFungibleItemList<T>>::remove(collection_id, item_id);2124            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2125        }2126        else {2127            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2128        }21292130        Ok(())2131    }21322133    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2134        let collection_id = collection.id;21352136        let item = <NftItemList<T>>::get(collection_id, item_id)2137            .ok_or(Error::<T>::TokenNotFound)?;2138        Self::remove_token_index(collection_id, item_id, &item.owner)?;21392140        // update balance2141        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2142            .checked_sub(1)2143            .ok_or(Error::<T>::NumOverflow)?;2144        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2145        <NftItemList<T>>::remove(collection_id, item_id);2146        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21472148        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2149        Ok(())2150    }21512152    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2153        let collection_id = collection.id;21542155        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2156        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21572158        // update balance2159        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2160            .checked_sub(value)2161            .ok_or(Error::<T>::NumOverflow)?;2162        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21632164        if balance.value - value > 0 {2165            balance.value -= value;2166            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2167        }2168        else {2169            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2170        }21712172        Ok(())2173    }21742175    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2176        Ok(<CollectionById<T>>::get(collection_id)2177            .map(|collection| CollectionHandle {2178                id: collection_id,2179                collection2180            })2181            .ok_or(Error::<T>::CollectionNotFound)?)2182    }21832184    fn save_collection(collection: CollectionHandle<T>) {2185        <CollectionById<T>>::insert(collection.id, collection.collection);2186    }21872188    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: T::AccountId) -> DispatchResult {2189        ensure!(2190            subject == target_collection.owner,2191            Error::<T>::NoPermission2192        );21932194        Ok(())2195    }21962197    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: T::AccountId) -> bool {2198        subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2199    }22002201    fn check_owner_or_admin_permissions(2202        collection: &CollectionHandle<T>,2203        subject: T::AccountId,2204    ) -> DispatchResult {2205        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22062207        Ok(())2208    }22092210    fn owned_amount(2211        subject: T::AccountId,2212        target_collection: &CollectionHandle<T>,2213        item_id: TokenId,2214    ) -> Option<u128> {2215        let collection_id = target_collection.id;22162217        match target_collection.mode {2218            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == subject)2219                .then(|| 1),2220            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject)2221                .value),2222            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2223                .owner2224                .iter()2225                .find(|i| i.owner == subject)2226                .map(|i| i.fraction),2227            CollectionMode::Invalid => None,2228        }2229    }22302231    fn is_item_owner(subject: T::AccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2232        match target_collection.mode {2233            CollectionMode::Fungible(_) => true,2234            _ => Self::owned_amount(subject, target_collection, item_id).is_some(),2235        }2236    }22372238    fn check_white_list(collection: &CollectionHandle<T>, address: &T::AccountId) -> DispatchResult {2239        let collection_id = collection.id;22402241        let mes = Error::<T>::AddresNotInWhiteList;2242        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);22432244        Ok(())2245    }22462247    /// Check if token exists. In case of Fungible, check if there is an entry for 2248    /// the owner in fungible balances double map2249    fn token_exists(2250        target_collection: &CollectionHandle<T>,2251        item_id: TokenId,2252    ) -> DispatchResult {2253        let collection_id = target_collection.id;2254        let exists = match target_collection.mode2255        {2256            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2257            CollectionMode::Fungible(_)  => true,2258            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2259            _ => false2260        };22612262        ensure!(exists == true, Error::<T>::TokenNotFound);2263        Ok(())2264    }22652266    fn transfer_fungible(2267        collection: &CollectionHandle<T>,2268        value: u128,2269        owner: &T::CrossAccountId,2270        recipient: &T::CrossAccountId,2271    ) -> DispatchResult {2272        let collection_id = collection.id;22732274        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2275        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);22762277        // Send balance to recipient (updates balanceOf of recipient)2278        Self::add_fungible_item(collection, recipient, value)?;22792280        // update balanceOf of sender2281        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);22822283        // Reduce or remove sender2284        if balance.value == value {2285            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2286        }2287        else {2288            balance.value -= value;2289            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2290        }22912292        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));22932294        Ok(())2295    }22962297    fn transfer_refungible(2298        collection: &CollectionHandle<T>,2299        item_id: TokenId,2300        value: u128,2301        owner: T::CrossAccountId,2302        new_owner: T::CrossAccountId,2303    ) -> DispatchResult {2304        let collection_id = collection.id;2305        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2306            .ok_or(Error::<T>::TokenNotFound)?;23072308        let item = full_item2309            .owner2310            .iter()2311            .filter(|i| i.owner == owner)2312            .next()2313            .ok_or(Error::<T>::TokenNotFound)?;2314        let amount = item.fraction;23152316        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23172318        // update balance2319        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2320            .checked_sub(value)2321            .ok_or(Error::<T>::NumOverflow)?;2322        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23232324        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2325            .checked_add(value)2326            .ok_or(Error::<T>::NumOverflow)?;2327        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23282329        let old_owner = item.owner.clone();2330        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23312332        // transfer2333        if amount == value && !new_owner_has_account {2334            // change owner2335            // new owner do not have account2336            let mut new_full_item = full_item.clone();2337            new_full_item2338                .owner2339                .iter_mut()2340                .find(|i| i.owner == owner)2341                .expect("old owner does present in refungible")2342                .owner = new_owner.clone();2343            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23442345            // update index collection2346            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2347        } else {2348            let mut new_full_item = full_item.clone();2349            new_full_item2350                .owner2351                .iter_mut()2352                .find(|i| i.owner == owner)2353                .expect("old owner does present in refungible")2354                .fraction -= value;23552356            // separate amount2357            if new_owner_has_account {2358                // new owner has account2359                new_full_item2360                    .owner2361                    .iter_mut()2362                    .find(|i| i.owner == new_owner)2363                    .expect("new owner has account")2364                    .fraction += value;2365            } else {2366                // new owner do not have account2367                new_full_item.owner.push(Ownership {2368                    owner: new_owner.clone(),2369                    fraction: value,2370                });2371                Self::add_token_index(collection_id, item_id, &new_owner)?;2372            }23732374            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2375        }23762377        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));23782379        Ok(())2380    }23812382    fn transfer_nft(2383        collection: &CollectionHandle<T>,2384        item_id: TokenId,2385        sender: T::CrossAccountId,2386        new_owner: T::CrossAccountId,2387    ) -> DispatchResult {2388        let collection_id = collection.id;2389        let mut item = <NftItemList<T>>::get(collection_id, item_id)2390            .ok_or(Error::<T>::TokenNotFound)?;23912392        ensure!(2393            sender == item.owner,2394            Error::<T>::MustBeTokenOwner2395        );23962397        // update balance2398        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2399            .checked_sub(1)2400            .ok_or(Error::<T>::NumOverflow)?;2401        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24022403        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2404            .checked_add(1)2405            .ok_or(Error::<T>::NumOverflow)?;2406        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24072408        // change owner2409        let old_owner = item.owner.clone();2410        item.owner = new_owner.clone();2411        <NftItemList<T>>::insert(collection_id, item_id, item);24122413        // update index collection2414        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24152416        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24172418        Ok(())2419    }2420    2421    fn set_re_fungible_variable_data(2422        collection: &CollectionHandle<T>,2423        item_id: TokenId,2424        data: Vec<u8>2425    ) -> DispatchResult {2426        let collection_id = collection.id;2427        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2428            .ok_or(Error::<T>::TokenNotFound)?;24292430        item.variable_data = data;24312432        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24332434        Ok(())2435    }24362437    fn set_nft_variable_data(2438        collection: &CollectionHandle<T>,2439        item_id: TokenId,2440        data: Vec<u8>2441    ) -> DispatchResult {2442        let collection_id = collection.id;2443        let mut item = <NftItemList<T>>::get(collection_id, item_id)2444            .ok_or(Error::<T>::TokenNotFound)?;2445        2446        item.variable_data = data;24472448        <NftItemList<T>>::insert(collection_id, item_id, item);2449        2450        Ok(())2451    }24522453    fn init_collection(item: &Collection<T>) {2454        // check params2455        assert!(2456            item.decimal_points <= MAX_DECIMAL_POINTS,2457            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2458        );2459        assert!(2460            item.name.len() <= 64,2461            "Collection name can not be longer than 63 char"2462        );2463        assert!(2464            item.name.len() <= 256,2465            "Collection description can not be longer than 255 char"2466        );2467        assert!(2468            item.token_prefix.len() <= 16,2469            "Token prefix can not be longer than 15 char"2470        );24712472        // Generate next collection ID2473        let next_id = CreatedCollectionCount::get()2474            .checked_add(1)2475            .unwrap();24762477        CreatedCollectionCount::put(next_id);2478    }24792480    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2481        let current_index = <ItemListIndex>::get(collection_id)2482            .checked_add(1)2483            .unwrap();24842485        let item_owner = item.owner.clone();2486        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();24872488        <ItemListIndex>::insert(collection_id, current_index);24892490        // Update balance2491        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2492            .checked_add(1)2493            .unwrap();2494        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2495    }24962497    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2498        let current_index = <ItemListIndex>::get(collection_id)2499            .checked_add(1)2500            .unwrap();25012502        Self::add_token_index(collection_id, current_index, owner).unwrap();25032504        <ItemListIndex>::insert(collection_id, current_index);25052506        // Update balance2507        let new_balance = <Balance<T>>::get(collection_id, owner)2508            .checked_add(item.value)2509            .unwrap();2510        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2511    }25122513    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2514        let current_index = <ItemListIndex>::get(collection_id)2515            .checked_add(1)2516            .unwrap();25172518        let value = item.owner.first().unwrap().fraction;2519        let owner = item.owner.first().unwrap().owner.clone();25202521        Self::add_token_index(collection_id, current_index, &owner).unwrap();25222523        <ItemListIndex>::insert(collection_id, current_index);25242525        // Update balance2526        let new_balance = <Balance<T>>::get(collection_id, &owner)2527            .checked_add(value)2528            .unwrap();2529        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2530    }25312532    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2533        // add to account limit2534        if <AccountItemCount<T>>::contains_key(owner) {25352536            // bound Owned tokens by a single address2537            let count = <AccountItemCount<T>>::get(owner);2538            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25392540            <AccountItemCount<T>>::insert(owner.clone(), count2541                .checked_add(1)2542                .ok_or(Error::<T>::NumOverflow)?);2543        }2544        else {2545            <AccountItemCount<T>>::insert(owner.clone(), 1);2546        }25472548        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2549        if list_exists {2550            let mut list = <AddressTokens<T>>::get(collection_id, owner);2551            let item_contains = list.contains(&item_index.clone());25522553            if !item_contains {2554                list.push(item_index.clone());2555            }25562557            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2558        } else {2559            let mut itm = Vec::new();2560            itm.push(item_index.clone());2561            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2562        }25632564        Ok(())2565    }25662567    fn remove_token_index(2568        collection_id: CollectionId,2569        item_index: TokenId,2570        owner: &T::AccountId,2571    ) -> DispatchResult {25722573        // update counter2574        <AccountItemCount<T>>::insert(owner.clone(), 2575            <AccountItemCount<T>>::get(owner)2576            .checked_sub(1)2577            .ok_or(Error::<T>::NumOverflow)?);257825792580        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2581        if list_exists {2582            let mut list = <AddressTokens<T>>::get(collection_id, owner);2583            let item_contains = list.contains(&item_index.clone());25842585            if item_contains {2586                list.retain(|&item| item != item_index);2587                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2588            }2589        }25902591        Ok(())2592    }25932594    fn move_token_index(2595        collection_id: CollectionId,2596        item_index: TokenId,2597        old_owner: &T::AccountId,2598        new_owner: &T::AccountId,2599    ) -> DispatchResult {2600        Self::remove_token_index(collection_id, item_index, old_owner)?;2601        Self::add_token_index(collection_id, item_index, new_owner)?;26022603        Ok(())2604    }2605    2606    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2607        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26082609        Ok(())2610    }2611}26122613////////////////////////////////////////////////////////////////////////////////////////////////////2614// Economic models2615// #region26162617/// Fee multiplier.2618pub type Multiplier = FixedU128;26192620type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26212622/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2623/// in the queue.2624#[derive(Encode, Decode, Clone, Eq, PartialEq)]2625pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26262627impl<T: Config + Send + Sync> sp_std::fmt::Debug 2628    for ChargeTransactionPayment<T>2629{2630	#[cfg(feature = "std")]2631	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2632		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2633	}2634	#[cfg(not(feature = "std"))]2635	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2636		Ok(())2637	}2638}26392640impl<T: Config> ChargeTransactionPayment<T>2641where2642    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2643    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2644    T::AccountId: AsRef<[u8]>,2645    T::AccountId: UncheckedFrom<T::Hash>,2646{2647    fn traditional_fee(2648        len: usize,2649        info: &DispatchInfoOf<T::Call>,2650        tip: BalanceOf<T>,2651    ) -> BalanceOf<T>2652    where2653        T::Call: Dispatchable<Info = DispatchInfo>,2654    {2655        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2656    }26572658	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2659        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2660        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2661        let len_saturation = max_block_length as u64 / (len as u64).max(1);2662        let coefficient: BalanceOf<T> = weight_saturation2663            .min(len_saturation)2664            .saturated_into::<BalanceOf<T>>();2665        final_fee2666            .saturating_mul(coefficient)2667            .saturated_into::<TransactionPriority>()2668    }26692670    fn withdraw_fee(2671        &self,2672        who: &T::AccountId,2673        call: &T::Call,2674        info: &DispatchInfoOf<T::Call>,2675        len: usize,2676	) -> Result<2677		(2678			BalanceOf<T>,2679			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2680		),2681		TransactionValidityError,2682	> {2683        let tip = self.0;26842685        let fee = Self::traditional_fee(len, info, tip);26862687        // Only mess with balances if fee is not zero.2688        if fee.is_zero() {2689            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2690			.map(|i| (fee, i));2691        }26922693        // Determine who is paying transaction fee based on ecnomic model2694        // Parse call to extract collection ID and access collection sponsor2695        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2696            Some(Call::create_item(collection_id, _owner, _properties)) => {2697                let collection = <CollectionById<T>>::get(collection_id)?;26982699                // sponsor timeout2700                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27012702                let limit = collection.limits.sponsor_transfer_timeout;2703                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2704                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2705                    let limit_time = last_tx_block + limit.into();2706                    if block_number <= limit_time {2707                        return None;2708                    }2709                }2710                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27112712                // check free create limit2713                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2714                    collection.sponsorship.sponsor()2715                        .cloned()2716                } else {2717                    None2718                }2719            }2720            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2721                let collection = <CollectionById<T>>::get(collection_id)?;2722                2723                let mut sponsor_transfer = false;2724                if collection.sponsorship.confirmed() {27252726                    let collection_limits = collection.limits;2727                    let collection_mode = collection.mode;2728    2729                    // sponsor timeout2730                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2731                    sponsor_transfer = match collection_mode {2732                        CollectionMode::NFT => {2733    2734                            // get correct limit2735                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2736                                collection_limits.sponsor_transfer_timeout2737                            } else {2738                                ChainLimit::get().nft_sponsor_transfer_timeout2739                            };2740    2741                            let mut sponsored = true;2742                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2743                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2744                                let limit_time = last_tx_block + limit.into();2745                                if block_number <= limit_time {2746                                    sponsored = false;2747                                }2748                            }2749                            if sponsored {2750                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2751                            }27522753                            sponsored2754                        }2755                        CollectionMode::Fungible(_) => {2756    2757                            // get correct limit2758                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2759                                collection_limits.sponsor_transfer_timeout2760                            } else {2761                                ChainLimit::get().fungible_sponsor_transfer_timeout2762                            };2763    2764                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2765                            let mut sponsored = true;2766                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2767                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2768                                let limit_time = last_tx_block + limit.into();2769                                if block_number <= limit_time {2770                                    sponsored = false;2771                                }2772                            }2773                            if sponsored {2774                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2775                            }27762777                            sponsored2778                        }2779                        CollectionMode::ReFungible => {2780    2781                            // get correct limit2782                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2783                                collection_limits.sponsor_transfer_timeout2784                            } else {2785                                ChainLimit::get().refungible_sponsor_transfer_timeout2786                            };2787    2788                            let mut sponsored = true;2789                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2790                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2791                                let limit_time = last_tx_block + limit.into();2792                                if block_number <= limit_time {2793                                    sponsored = false;2794                                }2795                            }2796                            if sponsored {2797                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2798                            }27992800                            sponsored2801                        }2802                        _ => {2803                            false2804                        },2805                    };2806                }28072808                if !sponsor_transfer {2809                    None2810                } else {2811                    collection.sponsorship.sponsor()2812                        .cloned()2813                }2814            }28152816            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2817                let mut sponsor_metadata_changes = false;28182819                let collection = <CollectionById<T>>::get(collection_id)?;28202821                if2822                    collection.sponsorship.confirmed() &&2823                    // Can't sponsor fungible collection, this tx will be rejected2824                    // as invalid2825                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2826                    data.len() <= collection.limits.sponsored_data_size as usize2827                {2828                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2829                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28302831                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2832                            .map(|last_block| block_number - last_block > rate_limit)2833                            .unwrap_or(true) 2834                        {2835                            sponsor_metadata_changes = true;2836                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2837                        }2838                    }2839                }28402841                if !sponsor_metadata_changes {2842                    None2843                } else {2844                    collection.sponsorship.sponsor().cloned()2845                }2846            }28472848            _ => None,2849        })();28502851        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2852            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {28532854                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());28552856                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2857                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2858                  2859                if !owned_contract && white_list_enabled {2860                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2861                        return Err(InvalidTransaction::Call.into());2862                    }2863                }2864            },2865            _ => {},2866        }28672868        // Sponsor smart contracts2869        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {28702871            // On instantiation: set the contract owner2872            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {28732874                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2875                    &who,2876                    code_hash,2877                    salt,2878                );2879                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28802881                None2882            },28832884            // On instantiation with code: set the contract owner2885            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {28862887                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2888                    &who,2889                    &T::Hashing::hash(&_code),2890                    _salt,2891                );28922893                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());28942895                None2896            }28972898            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2899            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29002901                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29022903                let mut sponsor_transfer = false;2904                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2905                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2906                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2907                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2908                    let limit_time = last_tx_block + rate_limit;29092910                    if block_number >= limit_time {2911                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2912                        sponsor_transfer = true;2913                    }2914                } else {2915                    sponsor_transfer = false;2916                }2917               2918                if sponsor_transfer {2919                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2920                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2921                            return Some(called_contract);2922                        }2923                    }2924                }29252926                None2927            },29282929            _ => None,2930        });29312932        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29332934		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2935			.map(|i| (fee, i))2936    }2937}293829392940impl<T: Config + Send + Sync> SignedExtension2941    for ChargeTransactionPayment<T>2942where2943    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2944    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2945    T::AccountId: AsRef<[u8]>,2946    T::AccountId: UncheckedFrom<T::Hash>,2947{2948    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2949    type AccountId = T::AccountId;2950    type Call = T::Call;2951    type AdditionalSigned = ();2952    type Pre = (2953        // tip2954        BalanceOf<T>,2955        // who pays fee2956        Self::AccountId,2957		// imbalance resulting from withdrawing the fee2958		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2959    );2960    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2961        Ok(())2962    }29632964    fn validate(2965        &self,2966        who: &Self::AccountId,2967        call: &Self::Call,2968        info: &DispatchInfoOf<Self::Call>,2969        len: usize,2970    ) -> TransactionValidity {2971		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2972		Ok(ValidTransaction {2973			priority: Self::get_priority(len, info, fee),2974			..Default::default()2975		})2976    }29772978    fn pre_dispatch(2979        self,2980        who: &Self::AccountId,2981        call: &Self::Call,2982        info: &DispatchInfoOf<Self::Call>,2983        len: usize,2984    ) -> Result<Self::Pre, TransactionValidityError> {2985        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2986        Ok((self.0, who.clone(), imbalance))2987    }29882989    fn post_dispatch(2990        pre: Self::Pre,2991        info: &DispatchInfoOf<Self::Call>,2992        post_info: &PostDispatchInfoOf<Self::Call>,2993        len: usize,2994        _result: &DispatchResult,2995    ) -> Result<(), TransactionValidityError> {2996		let (tip, who, imbalance) = pre;2997		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2998			len as u32,2999			info,3000			post_info,3001			tip,3002		);3003		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3004		Ok(())3005    }3006}30073008// #endregion30093010sp_api::decl_runtime_apis! {3011    pub trait NftApi {3012        /// Used for ethereum integration3013        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3014    }3015}