git.delta.rocks / unique-network / refs/commits / 6f2e0cc4002e

difftreelog

refactor combine sponsorship fields

Yaroslav Bolyukin2021-03-12parent: #bc76286.patch.diff
in: master

5 files changed

modifiednode/src/chain_spec.rsdiffbeforeafterboth
--- a/node/src/chain_spec.rs
+++ b/node/src/chain_spec.rs
@@ -193,8 +193,7 @@
                     mint_mode: false,
 					offchain_schema: vec![],
 					schema_version: SchemaVersion::default(),
-                    sponsor: get_account_id_from_seed::<sr25519::Public>("Alice"),
-                    sponsor_confirmed: true,
+                    sponsorship: SponsorshipState::Confirmed(get_account_id_from_seed::<sr25519::Public>("Alice")),
                     const_on_chain_schema: vec![],
 					variable_on_chain_schema: vec![],
 					limits: CollectionLimits::default()
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 std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};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_transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<AccountId> {129    pub owner: AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    pub token_limit: u32,185186    // Timeouts for item types in passed blocks187    pub sponsor_transfer_timeout: u32,188    pub owner_can_transfer: bool,189    pub owner_can_destroy: bool,190}191192impl Default for CollectionLimits {193    fn default() -> CollectionLimits {194        CollectionLimits { 195            account_token_ownership_limit: 10_000_000, 196            token_limit: u32::max_value(),197            sponsored_data_size: u32::MAX,198            sponsor_transfer_timeout: 14400,199            owner_can_transfer: true,200            owner_can_destroy: true201        }202    }203}204205#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]206#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]207pub struct ChainLimits {208    pub collection_numbers_limit: u32,209    pub account_token_ownership_limit: u32,210    pub collections_admins_limit: u64,211    pub custom_data_limit: u32,212213    // Timeouts for item types in passed blocks214    pub nft_sponsor_transfer_timeout: u32,215    pub fungible_sponsor_transfer_timeout: u32,216    pub refungible_sponsor_transfer_timeout: u32,217218    // Schema limits219    pub offchain_schema_limit: u32,220    pub variable_on_chain_schema_limit: u32,221    pub const_on_chain_schema_limit: u32,222}223224pub trait WeightInfo {225	fn create_collection() -> Weight;226	fn destroy_collection() -> Weight;227	fn add_to_white_list() -> Weight;228	fn remove_from_white_list() -> Weight;229    fn set_public_access_mode() -> Weight;230    fn set_mint_permission() -> Weight;231    fn change_collection_owner() -> Weight;232    fn add_collection_admin() -> Weight;233    fn remove_collection_admin() -> Weight;234    fn set_collection_sponsor() -> Weight;235    fn confirm_sponsorship() -> Weight;236    fn remove_collection_sponsor() -> Weight;237    fn create_item(s: usize) -> Weight;238    fn burn_item() -> Weight;239    fn transfer() -> Weight;240    fn approve() -> Weight;241    fn transfer_from() -> Weight;242    fn set_offchain_schema() -> Weight;243    fn set_const_on_chain_schema() -> Weight;244    fn set_variable_on_chain_schema() -> Weight;245    fn set_variable_meta_data() -> Weight;246    fn enable_contract_sponsoring() -> Weight;247    fn set_schema_version() -> Weight;248    fn set_chain_limits() -> Weight;249    fn set_contract_sponsoring_rate_limit() -> Weight;250    fn toggle_contract_white_list() -> Weight;251    fn add_to_contract_white_list() -> Weight;252    fn remove_from_contract_white_list() -> Weight;253    fn set_collection_limits() -> Weight;254}255256#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CreateNftData {259    pub const_data: Vec<u8>,260    pub variable_data: Vec<u8>,261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateFungibleData {266    pub value: u128,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateReFungibleData {272    pub const_data: Vec<u8>,273    pub variable_data: Vec<u8>,274    pub pieces: u128,275}276277#[derive(Encode, Decode, Debug, Clone, PartialEq)]278#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]279pub enum CreateItemData {280    NFT(CreateNftData),281    Fungible(CreateFungibleData),282    ReFungible(CreateReFungibleData),283}284285impl CreateItemData {286    pub fn len(&self) -> usize {287        let len = match self {288            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),289            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),290            _ => 0291        };292        293        return len;294    }295}296297impl From<CreateNftData> for CreateItemData {298    fn from(item: CreateNftData) -> Self {299        CreateItemData::NFT(item)300    }301}302303impl From<CreateReFungibleData> for CreateItemData {304    fn from(item: CreateReFungibleData) -> Self {305        CreateItemData::ReFungible(item)306    }307}308309impl From<CreateFungibleData> for CreateItemData {310    fn from(item: CreateFungibleData) -> Self {311        CreateItemData::Fungible(item)312    }313}314315316decl_error! {317	/// Error for non-fungible-token module.318	pub enum Error for Module<T: Config> {319        /// Total collections bound exceeded.320        TotalCollectionsLimitExceeded,321		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.322        CollectionDecimalPointLimitExceeded, 323        /// Collection name can not be longer than 63 char.324        CollectionNameLimitExceeded, 325        /// Collection description can not be longer than 255 char.326        CollectionDescriptionLimitExceeded, 327        /// Token prefix can not be longer than 15 char.328        CollectionTokenPrefixLimitExceeded,329        /// This collection does not exist.330        CollectionNotFound,331        /// Item not exists.332        TokenNotFound,333        /// Admin not found334        AdminNotFound,335        /// Arithmetic calculation overflow.336        NumOverflow,       337        /// Account already has admin role.338        AlreadyAdmin,  339        /// You do not own this collection.340        NoPermission,341        /// This address is not set as sponsor, use setCollectionSponsor first.342        ConfirmUnsetSponsorFail,343        /// Collection is not in mint mode.344        PublicMintingNotAllowed,345        /// Sender parameter and item owner must be equal.346        MustBeTokenOwner,347        /// Item balance not enough.348        TokenValueTooLow,349        /// Size of item is too large.350        NftSizeLimitExceeded,351        /// No approve found352        ApproveNotFound,353        /// Requested value more than approved.354        TokenValueNotEnough,355        /// Only approved addresses can call this method.356        ApproveRequired,357        /// Address is not in white list.358        AddresNotInWhiteList,359        /// Number of collection admins bound exceeded.360        CollectionAdminsLimitExceeded,361        /// Owned tokens by a single address bound exceeded.362        AddressOwnershipLimitExceeded,363        /// Length of items properties must be greater than 0.364        EmptyArgument,365        /// const_data exceeded data limit.366        TokenConstDataLimitExceeded,367        /// variable_data exceeded data limit.368        TokenVariableDataLimitExceeded,369        /// Not NFT item data used to mint in NFT collection.370        NotNftDataUsedToMintNftCollectionToken,371        /// Not Fungible item data used to mint in Fungible collection.372        NotFungibleDataUsedToMintFungibleCollectionToken,373        /// Not Re Fungible item data used to mint in Re Fungible collection.374        NotReFungibleDataUsedToMintReFungibleCollectionToken,375        /// Unexpected collection type.376        UnexpectedCollectionType,377        /// Can't store metadata in fungible tokens.378        CantStoreMetadataInFungibleTokens,379        /// Collection token limit exceeded380        CollectionTokenLimitExceeded,381        /// Account token limit exceeded per collection382        AccountTokenLimitExceeded,383        /// Collection limit bounds per collection exceeded384        CollectionLimitBoundsExceeded,385        /// Tried to enable permissions which are only permitted to be disabled386        OwnerPermissionsCantBeReverted,387        /// Schema data size limit bound exceeded388        SchemaDataLimitExceeded,389        /// Maximum refungibility exceeded390        WrongRefungiblePieces391	}392}393394pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {395    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;396397    /// Weight information for extrinsics in this pallet.398	type WeightInfo: WeightInfo;399}400401#[cfg(feature = "runtime-benchmarks")]402mod benchmarking;403404// #endregion405406// # Used definitions407//408// ## User control levels409//410// chain-controlled - key is uncontrolled by user411//                    i.e autoincrementing index412//                    can use non-cryptographic hash413// real - key is controlled by user414//        but it is hard to generate enough colliding values, i.e owner of signed txs415//        can use non-cryptographic hash416// controlled - key is completly controlled by users417//              i.e maps with mutable keys418//              should use cryptographic hash419//420// ## User control level downgrade reasons421//422// ?1 - chain-controlled -> controlled423//      collections/tokens can be destroyed, resulting in massive holes424// ?2 - chain-controlled -> controlled425//      same as ?1, but can be only added, resulting in easier exploitation426// ?3 - real -> controlled427//      no confirmation required, so addresses can be easily generated428decl_storage! {429    trait Store for Module<T: Config> as Nft {430431        //#region Private members432        /// Id of next collection433        CreatedCollectionCount: u32;434        /// Used for migrations435        ChainVersion: u64;436        /// Id of last collection token437        /// Collection id (controlled?1)438        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;439        //#endregion440441        //#region Chain limits struct442        pub ChainLimit get(fn chain_limit) config(): ChainLimits;443        //#endregion444445        //#region Bound counters446        /// Amount of collections destroyed, used for total amount tracking with447        /// CreatedCollectionCount448        DestroyedCollectionCount: u32;449        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)450        /// Account id (real)451        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;452        //#endregion453454        //#region Basic collections455        /// Collection info456        /// Collection id (controlled?1)457        pub Collection get(fn collection) config(): map hasher(blake2_128_concat) CollectionId => CollectionType<T::AccountId>;458        /// List of collection admins459        /// Collection id (controlled?2)460        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;461        /// Whitelisted collection users462        /// Collection id (controlled?2), user id (controlled?3)463        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;464        //#endregion465466        /// How many of collection items user have467        /// Collection id (controlled?2), account id (real)468        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;469470        /// Amount of items which spender can transfer out of owners account (via transferFrom)471        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))472        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;473474        //#region Item collections475        /// Collection id (controlled?2), token id (controlled?1)476        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;477        /// Collection id (controlled?2), owner (controlled?2)478        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;479        /// Collection id (controlled?2), token id (controlled?1)480        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;481        //#endregion482483        //#region Index list484        /// Collection id (controlled?2), tokens owner (controlled?2)485        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;486        //#endregion487488        //#region Tokens transfer rate limit baskets489        /// (Collection id (controlled?2), who created (real))490        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;491        /// Collection id (controlled?2), token id (controlled?2)492        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;493        /// Collection id (controlled?2), owning user (real)494        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;495        /// Collection id (controlled?2), token id (controlled?2)496        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;497        //#endregion498499        //#region Contract Sponsorship and Ownership500        /// Contract address (real)501        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;502        /// Contract address (real)503        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;504        /// (Contract address(real), caller (real))505        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;506        /// Contract address (real)507        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;508        /// Contract address (real)509        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 510        /// Contract address (real) => Whitelisted user (controlled?3)511        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 512        //#endregion513    }514    add_extra_genesis {515        build(|config: &GenesisConfig<T>| {516            // Modification of storage517            for (_num, _c) in &config.collection {518                <Module<T>>::init_collection(_c);519            }520521            for (_num, _c, _i) in &config.nft_item_id {522                <Module<T>>::init_nft_token(*_c, _i);523            }524525            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {526                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);527            }528529            for (_num, _c, _i) in &config.refungible_item_id {530                <Module<T>>::init_refungible_token(*_c, _i);531            }532        })533    }534}535536decl_event!(537    pub enum Event<T>538    where539        AccountId = <T as system::Config>::AccountId,540    {541        /// New collection was created542        /// 543        /// # Arguments544        /// 545        /// * collection_id: Globally unique identifier of newly created collection.546        /// 547        /// * mode: [CollectionMode] converted into u8.548        /// 549        /// * account_id: Collection owner.550        Created(CollectionId, u8, AccountId),551552        /// New item was created.553        /// 554        /// # Arguments555        /// 556        /// * collection_id: Id of the collection where item was created.557        /// 558        /// * item_id: Id of an item. Unique within the collection.559        ///560        /// * recipient: Owner of newly created item 561        ItemCreated(CollectionId, TokenId, AccountId),562563        /// Collection item was burned.564        /// 565        /// # Arguments566        /// 567        /// collection_id.568        /// 569        /// item_id: Identifier of burned NFT.570        ItemDestroyed(CollectionId, TokenId),571572        /// Item was transferred573        ///574        /// * collection_id: Id of collection to which item is belong575        ///576        /// * item_id: Id of an item577        ///578        /// * sender: Original owner of item579        ///580        /// * recipient: New owner of item581        ///582        /// * amount: Always 1 for NFT583        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),584    }585);586587decl_module! {588    pub struct Module<T: Config> for enum Call 589    where 590        origin: T::Origin591    {592        fn deposit_event() = default;593        type Error = Error<T>;594595        fn on_initialize(now: T::BlockNumber) -> Weight {596            0597        }598599        /// 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.600        /// 601        /// # Permissions602        /// 603        /// * Anyone.604        /// 605        /// # Arguments606        /// 607        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.608        /// 609        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.610        /// 611        /// * token_prefix: UTF-8 string with token prefix.612        /// 613        /// * mode: [CollectionMode] collection type and type dependent data.614        // returns collection ID615        #[weight = <T as Config>::WeightInfo::create_collection()]616        pub fn create_collection(origin,617                                 collection_name: Vec<u16>,618                                 collection_description: Vec<u16>,619                                 token_prefix: Vec<u8>,620                                 mode: CollectionMode) -> DispatchResult {621622            // Anyone can create a collection623            let who = ensure_signed(origin)?;624625            let decimal_points = match mode {626                CollectionMode::Fungible(points) => points,627                _ => 0628            };629630            let chain_limit = ChainLimit::get();631632            let created_count = CreatedCollectionCount::get();633            let destroyed_count = DestroyedCollectionCount::get();634635            // bound Total number of collections636            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);637638            // check params639            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);640            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);641            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);642            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);643644            // Generate next collection ID645            let next_id = created_count646                .checked_add(1)647                .ok_or(Error::<T>::NumOverflow)?;648649            CreatedCollectionCount::put(next_id);650651            let limits = CollectionLimits {652                sponsored_data_size: chain_limit.custom_data_limit,653                ..Default::default()654            };655656            // Create new collection657            let new_collection = CollectionType {658                owner: who.clone(),659                name: collection_name,660                mode: mode.clone(),661                mint_mode: false,662                access: AccessMode::Normal,663                description: collection_description,664                decimal_points: decimal_points,665                token_prefix: token_prefix,666                offchain_schema: Vec::new(),667                schema_version: SchemaVersion::ImageURL,668                sponsor: T::AccountId::default(),669                sponsor_confirmed: false,670                variable_on_chain_schema: Vec::new(),671                const_on_chain_schema: Vec::new(),672                limits,673            };674675            // Add new collection to map676            <Collection<T>>::insert(next_id, new_collection);677678            // call event679            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));680681            Ok(())682        }683684        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.685        /// 686        /// # Permissions687        /// 688        /// * Collection Owner.689        /// 690        /// # Arguments691        /// 692        /// * collection_id: collection to destroy.693        #[weight = <T as Config>::WeightInfo::destroy_collection()]694        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {695696            let sender = ensure_signed(origin)?;697            Self::check_owner_permissions(collection_id, sender)?;698699            let target_collection = <Collection<T>>::get(collection_id);700            if !target_collection.limits.owner_can_destroy {701                fail!(Error::<T>::NoPermission);702            }703704            <AddressTokens<T>>::remove_prefix(collection_id);705            <Allowances<T>>::remove_prefix(collection_id);706            <Balance<T>>::remove_prefix(collection_id);707            <ItemListIndex>::remove(collection_id);708            <AdminList<T>>::remove(collection_id);709            <Collection<T>>::remove(collection_id);710            <WhiteList<T>>::remove_prefix(collection_id);711712            <NftItemList<T>>::remove_prefix(collection_id);713            <FungibleItemList<T>>::remove_prefix(collection_id);714            <ReFungibleItemList<T>>::remove_prefix(collection_id);715716            <NftTransferBasket<T>>::remove_prefix(collection_id);717            <FungibleTransferBasket<T>>::remove_prefix(collection_id);718            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);719720            DestroyedCollectionCount::put(DestroyedCollectionCount::get()721                .checked_add(1)722                .ok_or(Error::<T>::NumOverflow)?);723724            Ok(())725        }726727        /// Add an address to white list.728        /// 729        /// # Permissions730        /// 731        /// * Collection Owner732        /// * Collection Admin733        /// 734        /// # Arguments735        /// 736        /// * collection_id.737        /// 738        /// * address.739        #[weight = <T as Config>::WeightInfo::add_to_white_list()]740        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{741742            let sender = ensure_signed(origin)?;743            Self::check_owner_or_admin_permissions(collection_id, sender)?;744745            <WhiteList<T>>::insert(collection_id, address, true);746            747            Ok(())748        }749750        /// Remove an address from white list.751        /// 752        /// # Permissions753        /// 754        /// * Collection Owner755        /// * Collection Admin756        /// 757        /// # Arguments758        /// 759        /// * collection_id.760        /// 761        /// * address.762        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]763        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{764765            let sender = ensure_signed(origin)?;766            Self::check_owner_or_admin_permissions(collection_id, sender)?;767768            <WhiteList<T>>::remove(collection_id, address);769770            Ok(())771        }772773        /// Toggle between normal and white list access for the methods with access for `Anyone`.774        /// 775        /// # Permissions776        /// 777        /// * Collection Owner.778        /// 779        /// # Arguments780        /// 781        /// * collection_id.782        /// 783        /// * mode: [AccessMode]784        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]785        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult786        {787            let sender = ensure_signed(origin)?;788789            Self::check_owner_permissions(collection_id, sender)?;790            let mut target_collection = <Collection<T>>::get(collection_id);791            target_collection.access = mode;792            <Collection<T>>::insert(collection_id, target_collection);793794            Ok(())795        }796797        /// Allows Anyone to create tokens if:798        /// * White List is enabled, and799        /// * Address is added to white list, and800        /// * This method was called with True parameter801        /// 802        /// # Permissions803        /// * Collection Owner804        ///805        /// # Arguments806        /// 807        /// * collection_id.808        /// 809        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.810        #[weight = <T as Config>::WeightInfo::set_mint_permission()]811        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult812        {813            let sender = ensure_signed(origin)?;814815            Self::check_owner_permissions(collection_id, sender)?;816            let mut target_collection = <Collection<T>>::get(collection_id);817            target_collection.mint_mode = mint_permission;818            <Collection<T>>::insert(collection_id, target_collection);819820            Ok(())821        }822823        /// Change the owner of the collection.824        /// 825        /// # Permissions826        /// 827        /// * Collection Owner.828        /// 829        /// # Arguments830        /// 831        /// * collection_id.832        /// 833        /// * new_owner.834        #[weight = <T as Config>::WeightInfo::change_collection_owner()]835        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {836837            let sender = ensure_signed(origin)?;838            Self::check_owner_permissions(collection_id, sender)?;839            let mut target_collection = <Collection<T>>::get(collection_id);840            target_collection.owner = new_owner;841            <Collection<T>>::insert(collection_id, target_collection);842843            Ok(())844        }845846        /// Adds an admin of the Collection.847        /// 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. 848        /// 849        /// # Permissions850        /// 851        /// * Collection Owner.852        /// * Collection Admin.853        /// 854        /// # Arguments855        /// 856        /// * collection_id: ID of the Collection to add admin for.857        /// 858        /// * new_admin_id: Address of new admin to add.859        #[weight = <T as Config>::WeightInfo::add_collection_admin()]860        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {861862            let sender = ensure_signed(origin)?;863            Self::check_owner_or_admin_permissions(collection_id, sender)?;864            let mut admin_arr: Vec<T::AccountId> = Vec::new();865866            if <AdminList<T>>::contains_key(collection_id)867            {868                admin_arr = <AdminList<T>>::get(collection_id);869                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);870            }871872            // Number of collection admins873            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);874875            admin_arr.push(new_admin_id);876            <AdminList<T>>::insert(collection_id, admin_arr);877878            Ok(())879        }880881        /// 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.882        ///883        /// # Permissions884        /// 885        /// * Collection Owner.886        /// * Collection Admin.887        /// 888        /// # Arguments889        /// 890        /// * collection_id: ID of the Collection to remove admin for.891        /// 892        /// * account_id: Address of admin to remove.893        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]894        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {895896            let sender = ensure_signed(origin)?;897            Self::check_owner_or_admin_permissions(collection_id, sender)?;898            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);899900            let mut admin_arr = <AdminList<T>>::get(collection_id);901            admin_arr.retain(|i| *i != account_id);902            <AdminList<T>>::insert(collection_id, admin_arr);903904            Ok(())905        }906907        /// # Permissions908        /// 909        /// * Collection Owner910        /// 911        /// # Arguments912        /// 913        /// * collection_id.914        /// 915        /// * new_sponsor.916        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]917        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {918919            let sender = ensure_signed(origin)?;920            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);921922            let mut target_collection = <Collection<T>>::get(collection_id);923            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);924925            target_collection.sponsor = new_sponsor;926            target_collection.sponsor_confirmed = false;927            <Collection<T>>::insert(collection_id, target_collection);928929            Ok(())930        }931932        /// # Permissions933        /// 934        /// * Sponsor.935        /// 936        /// # Arguments937        /// 938        /// * collection_id.939        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]940        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {941942            let sender = ensure_signed(origin)?;943            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);944945            let mut target_collection = <Collection<T>>::get(collection_id);946            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);947948            target_collection.sponsor_confirmed = true;949            <Collection<T>>::insert(collection_id, target_collection);950951            Ok(())952        }953954        /// Switch back to pay-per-own-transaction model.955        ///956        /// # Permissions957        ///958        /// * Collection owner.959        /// 960        /// # Arguments961        /// 962        /// * collection_id.963        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]964        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {965966            let sender = ensure_signed(origin)?;967            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);968969            let mut target_collection = <Collection<T>>::get(collection_id);970            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);971972            target_collection.sponsor = T::AccountId::default();973            target_collection.sponsor_confirmed = false;974            <Collection<T>>::insert(collection_id, target_collection);975976            Ok(())977        }978979        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.980        /// 981        /// # Permissions982        /// 983        /// * Collection Owner.984        /// * Collection Admin.985        /// * Anyone if986        ///     * White List is enabled, and987        ///     * Address is added to white list, and988        ///     * MintPermission is enabled (see SetMintPermission method)989        /// 990        /// # Arguments991        /// 992        /// * collection_id: ID of the collection.993        /// 994        /// * owner: Address, initial owner of the NFT.995        ///996        /// * data: Token data to store on chain.997        // #[weight =998        // (130_000_000 as Weight)999        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1000        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1001        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]10021003        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1004        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {10051006            let sender = ensure_signed(origin)?;10071008            Self::collection_exists(collection_id)?;10091010            let target_collection = <Collection<T>>::get(collection_id);10111012            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;1013            Self::validate_create_item_args(&target_collection, &data)?;1014            Self::create_item_no_validation(collection_id, owner, data)?;10151016            Ok(())1017        }10181019        /// This method creates multiple instances of NFT Collection created with CreateCollection method.1020        /// 1021        /// # Permissions1022        /// 1023        /// * Collection Owner.1024        /// * Collection Admin.1025        /// * Anyone if1026        ///     * White List is enabled, and1027        ///     * Address is added to white list, and1028        ///     * MintPermission is enabled (see SetMintPermission method)1029        /// 1030        /// # Arguments1031        /// 1032        /// * collection_id: ID of the collection.1033        /// 1034        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1035        /// 1036        /// * owner: Address, initial owner of the NFT.1037        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1038                               .map(|data| { data.len() })1039                               .sum())]1040        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10411042            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1043            let sender = ensure_signed(origin)?;10441045            Self::collection_exists(collection_id)?;1046            let target_collection = <Collection<T>>::get(collection_id);10471048            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10491050            for data in &items_data {1051                Self::validate_create_item_args(&target_collection, data)?;1052            }1053            for data in &items_data {1054                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1055            }10561057            Ok(())1058        }10591060        /// Destroys a concrete instance of NFT.1061        /// 1062        /// # Permissions1063        /// 1064        /// * Collection Owner.1065        /// * Collection Admin.1066        /// * Current NFT Owner.1067        /// 1068        /// # Arguments1069        /// 1070        /// * collection_id: ID of the collection.1071        /// 1072        /// * item_id: ID of NFT to burn.1073        #[weight = <T as Config>::WeightInfo::burn_item()]1074        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10751076            let sender = ensure_signed(origin)?;1077            Self::collection_exists(collection_id)?;10781079            // Transfer permissions check1080            let target_collection = <Collection<T>>::get(collection_id);1081            ensure!(1082                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1083                (1084                    target_collection.limits.owner_can_transfer &&1085                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1086                ),1087                Error::<T>::NoPermission1088            );10891090            if target_collection.access == AccessMode::WhiteList {1091                Self::check_white_list(collection_id, &sender)?;1092            }10931094            match target_collection.mode1095            {1096                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1097                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1098                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1099                _ => ()1100            };11011102            // call event1103            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));11041105            Ok(())1106        }11071108        /// Change ownership of the token.1109        /// 1110        /// # Permissions1111        /// 1112        /// * Collection Owner1113        /// * Collection Admin1114        /// * Current NFT owner1115        ///1116        /// # Arguments1117        /// 1118        /// * recipient: Address of token recipient.1119        /// 1120        /// * collection_id.1121        /// 1122        /// * item_id: ID of the item1123        ///     * Non-Fungible Mode: Required.1124        ///     * Fungible Mode: Ignored.1125        ///     * Re-Fungible Mode: Required.1126        /// 1127        /// * value: Amount to transfer.1128        ///     * Non-Fungible Mode: Ignored1129        ///     * Fungible Mode: Must specify transferred amount1130        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1131        #[weight = <T as Config>::WeightInfo::transfer()]1132        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1133            let sender = ensure_signed(origin)?;1134            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1135        }11361137        /// Set, change, or remove approved address to transfer the ownership of the NFT.1138        /// 1139        /// # Permissions1140        /// 1141        /// * Collection Owner1142        /// * Collection Admin1143        /// * Current NFT owner1144        /// 1145        /// # Arguments1146        /// 1147        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1148        /// 1149        /// * collection_id.1150        /// 1151        /// * item_id: ID of the item.1152        #[weight = <T as Config>::WeightInfo::approve()]1153        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11541155            let sender = ensure_signed(origin)?;11561157            Self::collection_exists(collection_id)?;1158            Self::token_exists(collection_id, item_id, &sender)?;11591160            // Transfer permissions check1161            let target_collection = <Collection<T>>::get(collection_id);1162            let allowance_limit = if target_collection.limits.owner_can_transfer &&1163                Self::is_owner_or_admin_permissions(1164                    collection_id,1165                    sender.clone(),1166                ) {1167                None1168            } else if let Some(amount) = Self::owned_amount(1169                sender.clone(),1170                collection_id,1171                item_id,1172            ) {1173                Some(amount)1174            } else {1175                fail!(Error::<T>::NoPermission);1176            };11771178            if target_collection.access == AccessMode::WhiteList {1179                Self::check_white_list(collection_id, &sender)?;1180                Self::check_white_list(collection_id, &spender)?;1181            }11821183            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1184            let mut allowance: u128 = amount;1185            if allowance_exists {1186                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1187            }1188            if let Some(limit) = allowance_limit {1189                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1190            }1191            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11921193            Ok(())1194        }1195        1196        /// 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.1197        /// 1198        /// # Permissions1199        /// * Collection Owner1200        /// * Collection Admin1201        /// * Current NFT owner1202        /// * Address approved by current NFT owner1203        /// 1204        /// # Arguments1205        /// 1206        /// * from: Address that owns token.1207        /// 1208        /// * recipient: Address of token recipient.1209        /// 1210        /// * collection_id.1211        /// 1212        /// * item_id: ID of the item.1213        /// 1214        /// * value: Amount to transfer.1215        #[weight = <T as Config>::WeightInfo::transfer_from()]1216        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12171218            let sender = ensure_signed(origin)?;1219            let mut appoved_transfer = false;12201221            // Check approval1222            let mut approval: u128 = 0;1223            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1224                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1225                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1226                appoved_transfer = true;1227            }12281229            let target_collection = <Collection<T>>::get(collection_id);12301231            // Limits check1232            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12331234            // Transfer permissions check         1235            ensure!(1236                appoved_transfer || 1237                (1238                    target_collection.limits.owner_can_transfer &&1239                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1240                ),1241                Error::<T>::NoPermission1242            );12431244            if target_collection.access == AccessMode::WhiteList {1245                Self::check_white_list(collection_id, &sender)?;1246                Self::check_white_list(collection_id, &recipient)?;1247            }12481249            // Reduce approval by transferred amount or remove if remaining approval drops to 01250            if approval.checked_sub(value).unwrap_or(0) > 0 {1251                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1252            }1253            else {1254                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1255            }12561257            match target_collection.mode1258            {1259                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1260                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1261                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1262                _ => ()1263            };12641265            Ok(())1266        }12671268        // #[weight = 0]1269        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12701271        //     // let no_perm_mes = "You do not have permissions to modify this collection";1272        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1273        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1274        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12751276        //     // // on_nft_received  call12771278        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12791280        //     Ok(())1281        // }12821283        /// Set off-chain data schema.1284        /// 1285        /// # Permissions1286        /// 1287        /// * Collection Owner1288        /// * Collection Admin1289        /// 1290        /// # Arguments1291        /// 1292        /// * collection_id.1293        /// 1294        /// * schema: String representing the offchain data schema.1295        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1296        pub fn set_variable_meta_data (1297            origin,1298            collection_id: CollectionId,1299            item_id: TokenId,1300            data: Vec<u8>1301        ) -> DispatchResult {1302            let sender = ensure_signed(origin)?;1303            1304            Self::collection_exists(collection_id)?;1305            Self::token_exists(collection_id, item_id, &sender)?;13061307            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13081309            // Modify permissions check1310            let target_collection = <Collection<T>>::get(collection_id);1311            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1312                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1313                Error::<T>::NoPermission);13141315            match target_collection.mode1316            {1317                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1318                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1319                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1320                _ => fail!(Error::<T>::UnexpectedCollectionType)1321            };13221323            Ok(())1324        }1325 1326        /// Set schema standard1327        /// ImageURL1328        /// Unique1329        /// 1330        /// # Permissions1331        /// 1332        /// * Collection Owner1333        /// * Collection Admin1334        /// 1335        /// # Arguments1336        /// 1337        /// * collection_id.1338        /// 1339        /// * schema: SchemaVersion: enum1340        #[weight = <T as Config>::WeightInfo::set_schema_version()]1341        pub fn set_schema_version(1342            origin,1343            collection_id: CollectionId,1344            version: SchemaVersion1345        ) -> DispatchResult {1346            let sender = ensure_signed(origin)?;1347            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1348            let mut target_collection = <Collection<T>>::get(collection_id);1349            target_collection.schema_version = version;1350            <Collection<T>>::insert(collection_id, target_collection);13511352            Ok(())1353        }13541355        /// Set off-chain data schema.1356        /// 1357        /// # Permissions1358        /// 1359        /// * Collection Owner1360        /// * Collection Admin1361        /// 1362        /// # Arguments1363        /// 1364        /// * collection_id.1365        /// 1366        /// * schema: String representing the offchain data schema.1367        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1368        pub fn set_offchain_schema(1369            origin,1370            collection_id: CollectionId,1371            schema: Vec<u8>1372        ) -> DispatchResult {1373            let sender = ensure_signed(origin)?;1374            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13751376            // check schema limit1377            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13781379            let mut target_collection = <Collection<T>>::get(collection_id);1380            target_collection.offchain_schema = schema;1381            <Collection<T>>::insert(collection_id, target_collection);13821383            Ok(())1384        }13851386        /// Set const on-chain data schema.1387        /// 1388        /// # Permissions1389        /// 1390        /// * Collection Owner1391        /// * Collection Admin1392        /// 1393        /// # Arguments1394        /// 1395        /// * collection_id.1396        /// 1397        /// * schema: String representing the const on-chain data schema.1398        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1399        pub fn set_const_on_chain_schema (1400            origin,1401            collection_id: CollectionId,1402            schema: Vec<u8>1403        ) -> DispatchResult {1404            let sender = ensure_signed(origin)?;1405            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14061407            // check schema limit1408            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14091410            let mut target_collection = <Collection<T>>::get(collection_id);1411            target_collection.const_on_chain_schema = schema;1412            <Collection<T>>::insert(collection_id, target_collection);14131414            Ok(())1415        }14161417        /// Set variable on-chain data schema.1418        /// 1419        /// # Permissions1420        /// 1421        /// * Collection Owner1422        /// * Collection Admin1423        /// 1424        /// # Arguments1425        /// 1426        /// * collection_id.1427        /// 1428        /// * schema: String representing the variable on-chain data schema.1429        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1430        pub fn set_variable_on_chain_schema (1431            origin,1432            collection_id: CollectionId,1433            schema: Vec<u8>1434        ) -> DispatchResult {1435            let sender = ensure_signed(origin)?;1436            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14371438            // check schema limit1439            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14401441            let mut target_collection = <Collection<T>>::get(collection_id);1442            target_collection.variable_on_chain_schema = schema;1443            <Collection<T>>::insert(collection_id, target_collection);14441445            Ok(())1446        }14471448        // Sudo permissions function1449        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1450        pub fn set_chain_limits(1451            origin,1452            limits: ChainLimits1453        ) -> DispatchResult {14541455            #[cfg(not(feature = "runtime-benchmarks"))]1456            ensure_root(origin)?;14571458            <ChainLimit>::put(limits);1459            Ok(())1460        }14611462        /// Enable smart contract self-sponsoring.1463        /// 1464        /// # Permissions1465        /// 1466        /// * Contract Owner1467        /// 1468        /// # Arguments1469        /// 1470        /// * contract address1471        /// * enable flag1472        /// 1473        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1474        pub fn enable_contract_sponsoring(1475            origin,1476            contract_address: T::AccountId,1477            enable: bool1478        ) -> DispatchResult {14791480            let sender = ensure_signed(origin)?;14811482            #[cfg(feature = "runtime-benchmarks")]1483            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14841485            Self::ensure_contract_owned(sender, &contract_address)?;14861487            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1488            Ok(())1489        }14901491        /// Set the rate limit for contract sponsoring to specified number of blocks.1492        /// 1493        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1494        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1495        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1496        /// from contract endowment if there are at least B blocks between such transactions. 1497        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1498        /// 1499        /// # Permissions1500        /// 1501        /// * Contract Owner1502        /// 1503        /// # Arguments1504        /// 1505        /// -`contract_address`: Address of the contract to sponsor1506        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1507        /// 1508        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1509        pub fn set_contract_sponsoring_rate_limit(1510            origin,1511            contract_address: T::AccountId,1512            rate_limit: T::BlockNumber1513        ) -> DispatchResult {1514            let sender = ensure_signed(origin)?;15151516            #[cfg(feature = "runtime-benchmarks")]1517            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15181519            Self::ensure_contract_owned(sender, &contract_address)?;1520            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1521            Ok(())1522        }15231524        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1525        /// 1526        /// # Permissions1527        /// 1528        /// * Address that deployed smart contract.1529        /// 1530        /// # Arguments1531        /// 1532        /// -`contract_address`: Address of the contract.1533        /// 1534        /// - `enable`: .  1535        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1536        pub fn toggle_contract_white_list(1537            origin,1538            contract_address: T::AccountId,1539            enable: bool1540        ) -> DispatchResult {1541            let sender = ensure_signed(origin)?;15421543            #[cfg(feature = "runtime-benchmarks")]1544            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15451546            Self::ensure_contract_owned(sender, &contract_address)?;1547            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1548            Ok(())1549        }1550        1551        /// Add an address to smart contract white list.1552        /// 1553        /// # Permissions1554        /// 1555        /// * Address that deployed smart contract.1556        /// 1557        /// # Arguments1558        /// 1559        /// -`contract_address`: Address of the contract.1560        ///1561        /// -`account_address`: Address to add.1562        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1563        pub fn add_to_contract_white_list(1564            origin,1565            contract_address: T::AccountId,1566            account_address: T::AccountId1567        ) -> DispatchResult {1568            let sender = ensure_signed(origin)?;15691570            #[cfg(feature = "runtime-benchmarks")]1571            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1572            1573            Self::ensure_contract_owned(sender, &contract_address)?;      1574            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1575            Ok(())1576        }15771578        /// Remove an address from smart contract white list.1579        /// 1580        /// # Permissions1581        /// 1582        /// * Address that deployed smart contract.1583        /// 1584        /// # Arguments1585        /// 1586        /// -`contract_address`: Address of the contract.1587        ///1588        /// -`account_address`: Address to remove.1589        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1590        pub fn remove_from_contract_white_list(1591            origin,1592            contract_address: T::AccountId,1593            account_address: T::AccountId1594        ) -> DispatchResult {1595            let sender = ensure_signed(origin)?;15961597            #[cfg(feature = "runtime-benchmarks")]1598            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15991600            Self::ensure_contract_owned(sender, &contract_address)?;1601            <ContractWhiteList<T>>::remove(contract_address, account_address);1602            Ok(())1603        }16041605        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1606        pub fn set_collection_limits(1607            origin,1608            collection_id: u32,1609            new_limits: CollectionLimits,1610        ) -> DispatchResult {1611            let sender = ensure_signed(origin)?;1612            Self::check_owner_permissions(collection_id, sender.clone())?;1613            let mut target_collection = <Collection<T>>::get(collection_id);1614            let old_limits = target_collection.limits;1615            let chain_limits = ChainLimit::get();16161617            // collection bounds1618            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1619                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1620                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1621                Error::<T>::CollectionLimitBoundsExceeded);16221623            // token_limit   check  prev1624            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1625            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16261627            ensure!(1628                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1629                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1630                Error::<T>::OwnerPermissionsCantBeReverted,1631            );16321633            target_collection.limits = new_limits;1634            <Collection<T>>::insert(collection_id, target_collection);16351636            Ok(())1637        } 1638    }1639}16401641impl<T: Config> Module<T> {16421643    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {16441645        let target_collection = <Collection<T>>::get(collection_id);16461647        // Limits check1648        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16491650        // Transfer permissions check1651        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1652            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1653            Error::<T>::NoPermission);16541655        if target_collection.access == AccessMode::WhiteList {1656            Self::check_white_list(collection_id, &sender)?;1657            Self::check_white_list(collection_id, &recipient)?;1658        }16591660        match target_collection.mode1661        {1662            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1663            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1664            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1665            _ => ()1666        };16671668        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16691670        Ok(())1671    }167216731674    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {16751676        // check token limit and account token limit1677        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1678        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1679        1680        Ok(())1681    }16821683    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16841685        // check token limit and account token limit1686        let total_items: u32 = ItemListIndex::get(collection_id);1687        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1688        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1689        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16901691        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1692            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1693            Self::check_white_list(collection_id, owner)?;1694            Self::check_white_list(collection_id, sender)?;1695        }16961697        Ok(())1698    }16991700    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1701        match target_collection.mode1702        {1703            CollectionMode::NFT => {1704                if let CreateItemData::NFT(data) = data {1705                    // check sizes1706                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1707                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1708                } else {1709                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1710                }1711            },1712            CollectionMode::Fungible(_) => {1713                if let CreateItemData::Fungible(_) = data {1714                } else {1715                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1716                }1717            },1718            CollectionMode::ReFungible => {1719                if let CreateItemData::ReFungible(data) = data {17201721                    // check sizes1722                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1723                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17241725                    // Check refungibility limits1726                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1727                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1728                } else {1729                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1730                }1731            },1732            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1733        };17341735        Ok(())1736    }17371738    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1739        match data1740        {1741            CreateItemData::NFT(data) => {1742                let item = NftItemType {1743                    owner: owner.clone(),1744                    const_data: data.const_data,1745                    variable_data: data.variable_data1746                };17471748                Self::add_nft_item(collection_id, item)?;1749            },1750            CreateItemData::Fungible(data) => {1751                Self::add_fungible_item(collection_id, &owner, data.value)?;1752            },1753            CreateItemData::ReFungible(data) => {1754                let mut owner_list = Vec::new();1755                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17561757                let item = ReFungibleItemType {1758                    owner: owner_list,1759                    const_data: data.const_data,1760                    variable_data: data.variable_data1761                };17621763                Self::add_refungible_item(collection_id, item)?;1764            }1765        };17661767        // call event1768        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17691770        Ok(())1771    }17721773    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17741775        // Does new owner already have an account?1776        let mut balance: u128 = 0;1777        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1778            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1779        } 17801781        // Mint 1782        let item = FungibleItemType {1783            value: balance + value1784        };1785        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17861787        // Update balance1788        let new_balance = <Balance<T>>::get(collection_id, owner)1789            .checked_add(value)1790            .ok_or(Error::<T>::NumOverflow)?;1791        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17921793        Ok(())1794    }17951796    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1797        let current_index = <ItemListIndex>::get(collection_id)1798            .checked_add(1)1799            .ok_or(Error::<T>::NumOverflow)?;1800        let itemcopy = item.clone();18011802        let value = item.owner.first().unwrap().fraction;1803        let owner = item.owner.first().unwrap().owner.clone();18041805        Self::add_token_index(collection_id, current_index, &owner)?;18061807        <ItemListIndex>::insert(collection_id, current_index);1808        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18091810        // Update balance1811        let new_balance = <Balance<T>>::get(collection_id, &owner)1812            .checked_add(value)1813            .ok_or(Error::<T>::NumOverflow)?;1814        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);18151816        Ok(())1817    }18181819    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1820        let current_index = <ItemListIndex>::get(collection_id)1821            .checked_add(1)1822            .ok_or(Error::<T>::NumOverflow)?;18231824        let item_owner = item.owner.clone();1825        Self::add_token_index(collection_id, current_index, &item.owner)?;18261827        <ItemListIndex>::insert(collection_id, current_index);1828        <NftItemList<T>>::insert(collection_id, current_index, item);18291830        // Update balance1831        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1832            .checked_add(1)1833            .ok_or(Error::<T>::NumOverflow)?;1834        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18351836        Ok(())1837    }18381839    fn burn_refungible_item(1840        collection_id: CollectionId,1841        item_id: TokenId,1842        owner: &T::AccountId,1843    ) -> DispatchResult {1844        ensure!(1845            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1846            Error::<T>::TokenNotFound1847        );1848        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1849        let rft_balance = token1850            .owner1851            .iter()1852            .filter(|&i| i.owner == *owner)1853            .next()1854            .unwrap();1855        Self::remove_token_index(collection_id, item_id, owner)?;18561857        // update balance1858        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1859            .checked_sub(rft_balance.fraction)1860            .ok_or(Error::<T>::NumOverflow)?;1861        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18621863        // Re-create owners list with sender removed1864        let index = token1865            .owner1866            .iter()1867            .position(|i| i.owner == *owner)1868            .unwrap();1869        token.owner.remove(index);1870        let owner_count = token.owner.len();18711872        // Burn the token completely if this was the last (only) owner1873        if owner_count == 0 {1874            <ReFungibleItemList<T>>::remove(collection_id, item_id);1875        }1876        else {1877            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1878        }18791880        Ok(())1881    }18821883    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1884        ensure!(1885            <NftItemList<T>>::contains_key(collection_id, item_id),1886            Error::<T>::TokenNotFound1887        );1888        let item = <NftItemList<T>>::get(collection_id, item_id);1889        Self::remove_token_index(collection_id, item_id, &item.owner)?;18901891        // update balance1892        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1893            .checked_sub(1)1894            .ok_or(Error::<T>::NumOverflow)?;1895        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1896        <NftItemList<T>>::remove(collection_id, item_id);18971898        Ok(())1899    }19001901    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1902        ensure!(1903            <FungibleItemList<T>>::contains_key(collection_id, owner),1904            Error::<T>::TokenNotFound1905        );1906        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1907        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19081909        // update balance1910        let new_balance = <Balance<T>>::get(collection_id, owner)1911            .checked_sub(value)1912            .ok_or(Error::<T>::NumOverflow)?;1913        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19141915        if balance.value - value > 0 {1916            balance.value -= value;1917            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1918        }1919        else {1920            <FungibleItemList<T>>::remove(collection_id, owner);1921        }19221923        Ok(())1924    }19251926    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1927        ensure!(1928            <Collection<T>>::contains_key(collection_id),1929            Error::<T>::CollectionNotFound1930        );1931        Ok(())1932    }19331934    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1935        Self::collection_exists(collection_id)?;19361937        let target_collection = <Collection<T>>::get(collection_id);1938        ensure!(1939            subject == target_collection.owner,1940            Error::<T>::NoPermission1941        );19421943        Ok(())1944    }19451946    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1947        let target_collection = <Collection<T>>::get(collection_id);1948        let mut result: bool = subject == target_collection.owner;1949        let exists = <AdminList<T>>::contains_key(collection_id);19501951        if !result & exists {1952            if <AdminList<T>>::get(collection_id).contains(&subject) {1953                result = true1954            }1955        }19561957        result1958    }19591960    fn check_owner_or_admin_permissions(1961        collection_id: CollectionId,1962        subject: T::AccountId,1963    ) -> DispatchResult {1964        Self::collection_exists(collection_id)?;1965        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19661967        ensure!(1968            result,1969            Error::<T>::NoPermission1970        );1971        Ok(())1972    }19731974    fn owned_amount(1975        subject: T::AccountId,1976        collection_id: CollectionId,1977        item_id: TokenId,1978    ) -> Option<u128> {1979        let target_collection = <Collection<T>>::get(collection_id);19801981        match target_collection.mode {1982            CollectionMode::NFT => {1983                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1984                    return Some(1)1985                }1986                None1987            },1988            CollectionMode::Fungible(_) => {1989                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1990                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1991                        .value);1992                }1993                None1994            },1995            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1996                .owner1997                .iter()1998                .find(|i| i.owner == subject)1999                .map(|i| i.fraction),2000            CollectionMode::Invalid => None,2001        }2002    }20032004    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {2005        let target_collection = <Collection<T>>::get(collection_id);20062007        match target_collection.mode {2008            CollectionMode::NFT => {2009                <NftItemList<T>>::get(collection_id, item_id).owner == subject2010            }2011            CollectionMode::Fungible(_) => {2012                <FungibleItemList<T>>::contains_key(collection_id, &subject)2013            }2014            CollectionMode::ReFungible => {2015                <ReFungibleItemList<T>>::get(collection_id, item_id)2016                    .owner2017                    .iter()2018                    .any(|i| i.owner == subject)2019            }2020            CollectionMode::Invalid => false,2021        }2022    }20232024    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {2025        let mes = Error::<T>::AddresNotInWhiteList;2026        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20272028        Ok(())2029    }20302031    /// Check if token exists. In case of Fungible, check if there is an entry for 2032    /// the owner in fungible balances double map2033    fn token_exists(2034        collection_id: CollectionId,2035        item_id: TokenId,2036        owner: &T::AccountId2037    ) -> DispatchResult {2038        let target_collection = <Collection<T>>::get(collection_id);2039        let exists = match target_collection.mode2040        {2041            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2042            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2043            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2044            _ => false2045        };20462047        ensure!(exists == true, Error::<T>::TokenNotFound);2048        Ok(())2049    }20502051    fn transfer_fungible(2052        collection_id: CollectionId,2053        value: u128,2054        owner: &T::AccountId,2055        recipient: &T::AccountId,2056    ) -> DispatchResult {2057        Self::token_exists(collection_id, 0, owner)?;20582059        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2060        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20612062        // Send balance to recipient (updates balanceOf of recipient)2063        Self::add_fungible_item(collection_id, recipient, value)?;20642065        // update balanceOf of sender2066        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20672068        // Reduce or remove sender2069        if balance.value == value {2070            <FungibleItemList<T>>::remove(collection_id, owner);2071        }2072        else {2073            balance.value -= value;2074            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2075        }20762077        Ok(())2078    }20792080    fn transfer_refungible(2081        collection_id: CollectionId,2082        item_id: TokenId,2083        value: u128,2084        owner: T::AccountId,2085        new_owner: T::AccountId,2086    ) -> DispatchResult {2087        Self::token_exists(collection_id, item_id, &owner)?;20882089        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2090        let item = full_item2091            .owner2092            .iter()2093            .filter(|i| i.owner == owner)2094            .next()2095            .ok_or(Error::<T>::NumOverflow)?;2096        let amount = item.fraction;20972098        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20992100        // update balance2101        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2102            .checked_sub(value)2103            .ok_or(Error::<T>::NumOverflow)?;2104        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21052106        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2107            .checked_add(value)2108            .ok_or(Error::<T>::NumOverflow)?;2109        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21102111        let old_owner = item.owner.clone();2112        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21132114        // transfer2115        if amount == value && !new_owner_has_account {2116            // change owner2117            // new owner do not have account2118            let mut new_full_item = full_item.clone();2119            new_full_item2120                .owner2121                .iter_mut()2122                .find(|i| i.owner == owner)2123                .unwrap()2124                .owner = new_owner.clone();2125            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21262127            // update index collection2128            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2129        } else {2130            let mut new_full_item = full_item.clone();2131            new_full_item2132                .owner2133                .iter_mut()2134                .find(|i| i.owner == owner)2135                .unwrap()2136                .fraction -= value;21372138            // separate amount2139            if new_owner_has_account {2140                // new owner has account2141                new_full_item2142                    .owner2143                    .iter_mut()2144                    .find(|i| i.owner == new_owner)2145                    .unwrap()2146                    .fraction += value;2147            } else {2148                // new owner do not have account2149                new_full_item.owner.push(Ownership {2150                    owner: new_owner.clone(),2151                    fraction: value,2152                });2153                Self::add_token_index(collection_id, item_id, &new_owner)?;2154            }21552156            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2157        }21582159        Ok(())2160    }21612162    fn transfer_nft(2163        collection_id: CollectionId,2164        item_id: TokenId,2165        sender: T::AccountId,2166        new_owner: T::AccountId,2167    ) -> DispatchResult {2168        Self::token_exists(collection_id, item_id, &sender)?;21692170        let mut item = <NftItemList<T>>::get(collection_id, item_id);21712172        ensure!(2173            sender == item.owner,2174            Error::<T>::MustBeTokenOwner2175        );21762177        // update balance2178        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2179            .checked_sub(1)2180            .ok_or(Error::<T>::NumOverflow)?;2181        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21822183        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2184            .checked_add(1)2185            .ok_or(Error::<T>::NumOverflow)?;2186        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21872188        // change owner2189        let old_owner = item.owner.clone();2190        item.owner = new_owner.clone();2191        <NftItemList<T>>::insert(collection_id, item_id, item);21922193        // update index collection2194        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21952196        Ok(())2197    }2198    2199    fn set_re_fungible_variable_data(2200        collection_id: CollectionId,2201        item_id: TokenId,2202        data: Vec<u8>2203    ) -> DispatchResult {2204        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);22052206        item.variable_data = data;22072208        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22092210        Ok(())2211    }22122213    fn set_nft_variable_data(2214        collection_id: CollectionId,2215        item_id: TokenId,2216        data: Vec<u8>2217    ) -> DispatchResult {2218        let mut item = <NftItemList<T>>::get(collection_id, item_id);2219        2220        item.variable_data = data;22212222        <NftItemList<T>>::insert(collection_id, item_id, item);2223        2224        Ok(())2225    }22262227    fn init_collection(item: &CollectionType<T::AccountId>) {2228        // check params2229        assert!(2230            item.decimal_points <= MAX_DECIMAL_POINTS,2231            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2232        );2233        assert!(2234            item.name.len() <= 64,2235            "Collection name can not be longer than 63 char"2236        );2237        assert!(2238            item.name.len() <= 256,2239            "Collection description can not be longer than 255 char"2240        );2241        assert!(2242            item.token_prefix.len() <= 16,2243            "Token prefix can not be longer than 15 char"2244        );22452246        // Generate next collection ID2247        let next_id = CreatedCollectionCount::get()2248            .checked_add(1)2249            .unwrap();22502251        CreatedCollectionCount::put(next_id);2252    }22532254    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2255        let current_index = <ItemListIndex>::get(collection_id)2256            .checked_add(1)2257            .unwrap();22582259        let item_owner = item.owner.clone();2260        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22612262        <ItemListIndex>::insert(collection_id, current_index);22632264        // Update balance2265        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2266            .checked_add(1)2267            .unwrap();2268        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2269    }22702271    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2272        let current_index = <ItemListIndex>::get(collection_id)2273            .checked_add(1)2274            .unwrap();22752276        Self::add_token_index(collection_id, current_index, owner).unwrap();22772278        <ItemListIndex>::insert(collection_id, current_index);22792280        // Update balance2281        let new_balance = <Balance<T>>::get(collection_id, owner)2282            .checked_add(item.value)2283            .unwrap();2284        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2285    }22862287    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2288        let current_index = <ItemListIndex>::get(collection_id)2289            .checked_add(1)2290            .unwrap();22912292        let value = item.owner.first().unwrap().fraction;2293        let owner = item.owner.first().unwrap().owner.clone();22942295        Self::add_token_index(collection_id, current_index, &owner).unwrap();22962297        <ItemListIndex>::insert(collection_id, current_index);22982299        // Update balance2300        let new_balance = <Balance<T>>::get(collection_id, &owner)2301            .checked_add(value)2302            .unwrap();2303        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2304    }23052306    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {23072308        // add to account limit2309        if <AccountItemCount<T>>::contains_key(owner) {23102311            // bound Owned tokens by a single address2312            let count = <AccountItemCount<T>>::get(owner);2313            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23142315            <AccountItemCount<T>>::insert(owner.clone(), count2316                .checked_add(1)2317                .ok_or(Error::<T>::NumOverflow)?);2318        }2319        else {2320            <AccountItemCount<T>>::insert(owner.clone(), 1);2321        }23222323        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2324        if list_exists {2325            let mut list = <AddressTokens<T>>::get(collection_id, owner);2326            let item_contains = list.contains(&item_index.clone());23272328            if !item_contains {2329                list.push(item_index.clone());2330            }23312332            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2333        } else {2334            let mut itm = Vec::new();2335            itm.push(item_index.clone());2336            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2337        }23382339        Ok(())2340    }23412342    fn remove_token_index(2343        collection_id: CollectionId,2344        item_index: TokenId,2345        owner: &T::AccountId,2346    ) -> DispatchResult {23472348        // update counter2349        <AccountItemCount<T>>::insert(owner.clone(), 2350            <AccountItemCount<T>>::get(owner)2351            .checked_sub(1)2352            .ok_or(Error::<T>::NumOverflow)?);235323542355        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2356        if list_exists {2357            let mut list = <AddressTokens<T>>::get(collection_id, owner);2358            let item_contains = list.contains(&item_index.clone());23592360            if item_contains {2361                list.retain(|&item| item != item_index);2362                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2363            }2364        }23652366        Ok(())2367    }23682369    fn move_token_index(2370        collection_id: CollectionId,2371        item_index: TokenId,2372        old_owner: &T::AccountId,2373        new_owner: &T::AccountId,2374    ) -> DispatchResult {2375        Self::remove_token_index(collection_id, item_index, old_owner)?;2376        Self::add_token_index(collection_id, item_index, new_owner)?;23772378        Ok(())2379    }2380    2381    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2382        if <ContractOwner<T>>::contains_key(contract.clone()) {2383            let owner = <ContractOwner<T>>::get(contract);2384            ensure!(account == owner, Error::<T>::NoPermission);2385        } else {2386            fail!(Error::<T>::NoPermission);2387        }23882389        Ok(())2390    }2391}23922393////////////////////////////////////////////////////////////////////////////////////////////////////2394// Economic models2395// #region23962397/// Fee multiplier.2398pub type Multiplier = FixedU128;23992400type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24012402/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2403/// in the queue.2404#[derive(Encode, Decode, Clone, Eq, PartialEq)]2405pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);24062407impl<T: Config + Send + Sync> sp_std::fmt::Debug 2408    for ChargeTransactionPayment<T>2409{2410	#[cfg(feature = "std")]2411	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2412		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2413	}2414	#[cfg(not(feature = "std"))]2415	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2416		Ok(())2417	}2418}24192420impl<T: Config> ChargeTransactionPayment<T>2421where2422    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2423    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2424    T::AccountId: AsRef<[u8]>,2425    T::AccountId: UncheckedFrom<T::Hash>,2426{2427    fn traditional_fee(2428        len: usize,2429        info: &DispatchInfoOf<T::Call>,2430        tip: BalanceOf<T>,2431    ) -> BalanceOf<T>2432    where2433        T::Call: Dispatchable<Info = DispatchInfo>,2434    {2435        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2436    }24372438	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2439        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2440        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2441        let len_saturation = max_block_length as u64 / (len as u64).max(1);2442        let coefficient: BalanceOf<T> = weight_saturation2443            .min(len_saturation)2444            .saturated_into::<BalanceOf<T>>();2445        final_fee2446            .saturating_mul(coefficient)2447            .saturated_into::<TransactionPriority>()2448    }24492450    fn withdraw_fee(2451        &self,2452        who: &T::AccountId,2453        call: &T::Call,2454        info: &DispatchInfoOf<T::Call>,2455        len: usize,2456	) -> Result<2457		(2458			BalanceOf<T>,2459			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2460		),2461		TransactionValidityError,2462	> {2463        let tip = self.0;24642465        // Set fee based on call type. Creating collection costs 1 Unique.2466        // All other transactions have traditional fees so far2467        // let fee = match call.is_sub_type() {2468        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2469        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2470        //                                                 // _ => <BalanceOf<T>>::from(100)2471        // };2472        let fee = Self::traditional_fee(len, info, tip);24732474        // Only mess with balances if fee is not zero.2475        if fee.is_zero() {2476            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2477			.map(|i| (fee, i));2478        }24792480        // Determine who is paying transaction fee based on ecnomic model2481        // Parse call to extract collection ID and access collection sponsor2482        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2483            Some(Call::create_item(collection_id, _owner, _properties)) => {24842485                // sponsor timeout2486                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24872488                let limit = <Collection<T>>::get(collection_id).limits.sponsor_transfer_timeout;2489                let mut sponsored = true;2490                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2491                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2492                    let limit_time = last_tx_block + limit.into();2493                    if block_number <= limit_time {2494                        sponsored = false;2495                    }2496                }2497                if sponsored {2498                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2499                }25002501                // check free create limit2502                if (<Collection<T>>::get(collection_id).limits.sponsored_data_size >= (_properties.len() as u32)) &&2503                   (<Collection<T>>::get(collection_id).sponsor_confirmed) &&2504                   (sponsored)2505                {2506                    <Collection<T>>::get(collection_id).sponsor2507                } else {2508                    T::AccountId::default()2509                }2510            }2511            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2512                2513                let mut sponsor_transfer = false;2514                if <Collection<T>>::get(collection_id).sponsor_confirmed {25152516                    let collection_limits = <Collection<T>>::get(collection_id).limits;2517                    let collection_mode = <Collection<T>>::get(collection_id).mode;2518    2519                    // sponsor timeout2520                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2521                    sponsor_transfer = match collection_mode {2522                        CollectionMode::NFT => {2523    2524                            // get correct limit2525                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2526                                collection_limits.sponsor_transfer_timeout2527                            } else {2528                                ChainLimit::get().nft_sponsor_transfer_timeout2529                            };2530    2531                            let mut sponsored = true;2532                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2533                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2534                                let limit_time = last_tx_block + limit.into();2535                                if block_number <= limit_time {2536                                    sponsored = false;2537                                }2538                            }2539                            if sponsored {2540                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2541                            }25422543                            sponsored2544                        }2545                        CollectionMode::Fungible(_) => {2546    2547                            // get correct limit2548                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2549                                collection_limits.sponsor_transfer_timeout2550                            } else {2551                                ChainLimit::get().fungible_sponsor_transfer_timeout2552                            };2553    2554                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2555                            let mut sponsored = true;2556                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2557                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2558                                let limit_time = last_tx_block + limit.into();2559                                if block_number <= limit_time {2560                                    sponsored = false;2561                                }2562                            }2563                            if sponsored {2564                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2565                            }25662567                            sponsored2568                        }2569                        CollectionMode::ReFungible => {2570    2571                            // get correct limit2572                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2573                                collection_limits.sponsor_transfer_timeout2574                            } else {2575                                ChainLimit::get().refungible_sponsor_transfer_timeout2576                            };2577    2578                            let mut sponsored = true;2579                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2580                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2581                                let limit_time = last_tx_block + limit.into();2582                                if block_number <= limit_time {2583                                    sponsored = false;2584                                }2585                            }2586                            if sponsored {2587                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2588                            }25892590                            sponsored2591                        }2592                        _ => {2593                            false2594                        },2595                    };2596                }25972598                if !sponsor_transfer {2599                    T::AccountId::default()2600                } else {2601                    <Collection<T>>::get(collection_id).sponsor2602                }2603            }26042605            _ => T::AccountId::default(),2606        };26072608        // Sponsor smart contracts2609        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26102611            // On instantiation: set the contract owner2612            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26132614                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2615                    &who,2616                    code_hash,2617                    salt,2618                );2619                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26202621                T::AccountId::default()2622            },26232624            // On instantiation with code: set the contract owner2625            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {26262627                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2628                    &who,2629                    &T::Hashing::hash(&_code),2630                    _salt,2631                );26322633                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26342635                T::AccountId::default()2636            }26372638            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2639            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26402641                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26422643                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2644                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2645                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2646                  2647                if !owned_contract && white_list_enabled {2648                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2649                        return Err(InvalidTransaction::Call.into());2650                    }2651                }26522653                let mut sponsor_transfer = false;2654                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2655                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2656                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2657                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2658                    let limit_time = last_tx_block + rate_limit;26592660                    if block_number >= limit_time {2661                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2662                        sponsor_transfer = true;2663                    }2664                } else {2665                    sponsor_transfer = false;2666                }2667               2668                2669                let mut sp = T::AccountId::default();2670                if sponsor_transfer {2671                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2672                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2673                            sp = called_contract;2674                        }2675                    }2676                }26772678                sp2679            },26802681            _ => sponsor,2682        };26832684        let mut who_pays_fee: T::AccountId = sponsor.clone();2685        if sponsor == T::AccountId::default() {2686            who_pays_fee = who.clone();2687        }26882689		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2690			.map(|i| (fee, i))2691    }2692}269326942695impl<T: Config + Send + Sync> SignedExtension2696    for ChargeTransactionPayment<T>2697where2698    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2699    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2700    T::AccountId: AsRef<[u8]>,2701    T::AccountId: UncheckedFrom<T::Hash>,2702{2703    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2704    type AccountId = T::AccountId;2705    type Call = T::Call;2706    type AdditionalSigned = ();2707    type Pre = (2708        // tip2709        BalanceOf<T>,2710        // who pays fee2711        Self::AccountId,2712		// imbalance resulting from withdrawing the fee2713		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2714    );2715    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2716        Ok(())2717    }27182719    fn validate(2720        &self,2721        who: &Self::AccountId,2722        call: &Self::Call,2723        info: &DispatchInfoOf<Self::Call>,2724        len: usize,2725    ) -> TransactionValidity {2726		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2727		Ok(ValidTransaction {2728			priority: Self::get_priority(len, info, fee),2729			..Default::default()2730		})2731    }27322733    fn pre_dispatch(2734        self,2735        who: &Self::AccountId,2736        call: &Self::Call,2737        info: &DispatchInfoOf<Self::Call>,2738        len: usize,2739    ) -> Result<Self::Pre, TransactionValidityError> {2740        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2741        Ok((self.0, who.clone(), imbalance))2742    }27432744    fn post_dispatch(2745        pre: Self::Pre,2746        info: &DispatchInfoOf<Self::Call>,2747        post_info: &PostDispatchInfoOf<Self::Call>,2748        len: usize,2749        _result: &DispatchResult,2750    ) -> Result<(), TransactionValidityError> {2751		let (tip, who, imbalance) = pre;2752		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2753			len as u32,2754			info,2755			post_info,2756			tip,2757		);2758		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2759		Ok(())2760    }2761}27622763// #endregion
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 std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};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_transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Debug, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub enum SponsorshipState<AccountId> {129    /// The fees are applied to the transaction sender130    Disabled,131    Unconfirmed(AccountId),132    /// Transactions are sponsored by specified account133    Confirmed(AccountId),134}135136impl<AccountId> SponsorshipState<AccountId> {137    fn sponsor(&self) -> Option<&AccountId> {138        match self {139            Self::Confirmed(sponsor) => Some(sponsor),140            _ => None,141        }142    }143144    fn pending_sponsor(&self) -> Option<&AccountId> {145        match self {146            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),147            _ => None,148        }149    }150151    fn confirmed(&self) -> bool {152        matches!(self, Self::Confirmed(_))153    }154}155156impl<T> Default for SponsorshipState<T> {157    fn default() -> Self {158        Self::Disabled159    }160}161162#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]163#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]164pub struct CollectionType<AccountId> {165    pub owner: AccountId,166    pub mode: CollectionMode,167    pub access: AccessMode,168    pub decimal_points: DecimalPoints,169    pub name: Vec<u16>,        // 64 include null escape char170    pub description: Vec<u16>, // 256 include null escape char171    pub token_prefix: Vec<u8>, // 16 include null escape char172    pub mint_mode: bool,173    pub offchain_schema: Vec<u8>,174    pub schema_version: SchemaVersion,175    pub sponsorship: SponsorshipState<AccountId>,176    pub limits: CollectionLimits, // Collection private restrictions 177    pub variable_on_chain_schema: Vec<u8>, //178    pub const_on_chain_schema: Vec<u8>, //179}180181#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]182#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]183pub struct NftItemType<AccountId> {184    pub owner: AccountId,185    pub const_data: Vec<u8>,186    pub variable_data: Vec<u8>,187}188189#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]190#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]191pub struct FungibleItemType {192    pub value: u128,193}194195#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]196#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]197pub struct ReFungibleItemType<AccountId> {198    pub owner: Vec<Ownership<AccountId>>,199    pub const_data: Vec<u8>,200    pub variable_data: Vec<u8>,201}202203// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]204// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]205// pub struct VestingItem<AccountId, Moment> {206//     pub sender: AccountId,207//     pub recipient: AccountId,208//     pub collection_id: CollectionId,209//     pub item_id: TokenId,210//     pub amount: u64,211//     pub vesting_date: Moment,212// }213214#[derive(Encode, Decode, Debug, Clone, PartialEq)]215#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]216pub struct CollectionLimits {217    pub account_token_ownership_limit: u32,218    pub sponsored_data_size: u32,219    pub token_limit: u32,220221    // Timeouts for item types in passed blocks222    pub sponsor_transfer_timeout: u32,223    pub owner_can_transfer: bool,224    pub owner_can_destroy: bool,225}226227impl Default for CollectionLimits {228    fn default() -> CollectionLimits {229        CollectionLimits { 230            account_token_ownership_limit: 10_000_000, 231            token_limit: u32::max_value(),232            sponsored_data_size: u32::MAX,233            sponsor_transfer_timeout: 14400,234            owner_can_transfer: true,235            owner_can_destroy: true236        }237    }238}239240#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]241#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]242pub struct ChainLimits {243    pub collection_numbers_limit: u32,244    pub account_token_ownership_limit: u32,245    pub collections_admins_limit: u64,246    pub custom_data_limit: u32,247248    // Timeouts for item types in passed blocks249    pub nft_sponsor_transfer_timeout: u32,250    pub fungible_sponsor_transfer_timeout: u32,251    pub refungible_sponsor_transfer_timeout: u32,252253    // Schema limits254    pub offchain_schema_limit: u32,255    pub variable_on_chain_schema_limit: u32,256    pub const_on_chain_schema_limit: u32,257}258259pub trait WeightInfo {260	fn create_collection() -> Weight;261	fn destroy_collection() -> Weight;262	fn add_to_white_list() -> Weight;263	fn remove_from_white_list() -> Weight;264    fn set_public_access_mode() -> Weight;265    fn set_mint_permission() -> Weight;266    fn change_collection_owner() -> Weight;267    fn add_collection_admin() -> Weight;268    fn remove_collection_admin() -> Weight;269    fn set_collection_sponsor() -> Weight;270    fn confirm_sponsorship() -> Weight;271    fn remove_collection_sponsor() -> Weight;272    fn create_item(s: usize) -> Weight;273    fn burn_item() -> Weight;274    fn transfer() -> Weight;275    fn approve() -> Weight;276    fn transfer_from() -> Weight;277    fn set_offchain_schema() -> Weight;278    fn set_const_on_chain_schema() -> Weight;279    fn set_variable_on_chain_schema() -> Weight;280    fn set_variable_meta_data() -> Weight;281    fn enable_contract_sponsoring() -> Weight;282    fn set_schema_version() -> Weight;283    fn set_chain_limits() -> Weight;284    fn set_contract_sponsoring_rate_limit() -> Weight;285    fn toggle_contract_white_list() -> Weight;286    fn add_to_contract_white_list() -> Weight;287    fn remove_from_contract_white_list() -> Weight;288    fn set_collection_limits() -> Weight;289}290291#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]292#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]293pub struct CreateNftData {294    pub const_data: Vec<u8>,295    pub variable_data: Vec<u8>,296}297298#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]299#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]300pub struct CreateFungibleData {301    pub value: u128,302}303304#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]305#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]306pub struct CreateReFungibleData {307    pub const_data: Vec<u8>,308    pub variable_data: Vec<u8>,309    pub pieces: u128,310}311312#[derive(Encode, Decode, Debug, Clone, PartialEq)]313#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]314pub enum CreateItemData {315    NFT(CreateNftData),316    Fungible(CreateFungibleData),317    ReFungible(CreateReFungibleData),318}319320impl CreateItemData {321    pub fn len(&self) -> usize {322        let len = match self {323            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),324            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),325            _ => 0326        };327        328        return len;329    }330}331332impl From<CreateNftData> for CreateItemData {333    fn from(item: CreateNftData) -> Self {334        CreateItemData::NFT(item)335    }336}337338impl From<CreateReFungibleData> for CreateItemData {339    fn from(item: CreateReFungibleData) -> Self {340        CreateItemData::ReFungible(item)341    }342}343344impl From<CreateFungibleData> for CreateItemData {345    fn from(item: CreateFungibleData) -> Self {346        CreateItemData::Fungible(item)347    }348}349350351decl_error! {352	/// Error for non-fungible-token module.353	pub enum Error for Module<T: Config> {354        /// Total collections bound exceeded.355        TotalCollectionsLimitExceeded,356		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.357        CollectionDecimalPointLimitExceeded, 358        /// Collection name can not be longer than 63 char.359        CollectionNameLimitExceeded, 360        /// Collection description can not be longer than 255 char.361        CollectionDescriptionLimitExceeded, 362        /// Token prefix can not be longer than 15 char.363        CollectionTokenPrefixLimitExceeded,364        /// This collection does not exist.365        CollectionNotFound,366        /// Item not exists.367        TokenNotFound,368        /// Admin not found369        AdminNotFound,370        /// Arithmetic calculation overflow.371        NumOverflow,       372        /// Account already has admin role.373        AlreadyAdmin,  374        /// You do not own this collection.375        NoPermission,376        /// This address is not set as sponsor, use setCollectionSponsor first.377        ConfirmUnsetSponsorFail,378        /// Collection is not in mint mode.379        PublicMintingNotAllowed,380        /// Sender parameter and item owner must be equal.381        MustBeTokenOwner,382        /// Item balance not enough.383        TokenValueTooLow,384        /// Size of item is too large.385        NftSizeLimitExceeded,386        /// No approve found387        ApproveNotFound,388        /// Requested value more than approved.389        TokenValueNotEnough,390        /// Only approved addresses can call this method.391        ApproveRequired,392        /// Address is not in white list.393        AddresNotInWhiteList,394        /// Number of collection admins bound exceeded.395        CollectionAdminsLimitExceeded,396        /// Owned tokens by a single address bound exceeded.397        AddressOwnershipLimitExceeded,398        /// Length of items properties must be greater than 0.399        EmptyArgument,400        /// const_data exceeded data limit.401        TokenConstDataLimitExceeded,402        /// variable_data exceeded data limit.403        TokenVariableDataLimitExceeded,404        /// Not NFT item data used to mint in NFT collection.405        NotNftDataUsedToMintNftCollectionToken,406        /// Not Fungible item data used to mint in Fungible collection.407        NotFungibleDataUsedToMintFungibleCollectionToken,408        /// Not Re Fungible item data used to mint in Re Fungible collection.409        NotReFungibleDataUsedToMintReFungibleCollectionToken,410        /// Unexpected collection type.411        UnexpectedCollectionType,412        /// Can't store metadata in fungible tokens.413        CantStoreMetadataInFungibleTokens,414        /// Collection token limit exceeded415        CollectionTokenLimitExceeded,416        /// Account token limit exceeded per collection417        AccountTokenLimitExceeded,418        /// Collection limit bounds per collection exceeded419        CollectionLimitBoundsExceeded,420        /// Tried to enable permissions which are only permitted to be disabled421        OwnerPermissionsCantBeReverted,422        /// Schema data size limit bound exceeded423        SchemaDataLimitExceeded,424        /// Maximum refungibility exceeded425        WrongRefungiblePieces426	}427}428429pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {430    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;431432    /// Weight information for extrinsics in this pallet.433	type WeightInfo: WeightInfo;434}435436#[cfg(feature = "runtime-benchmarks")]437mod benchmarking;438439// #endregion440441// # Used definitions442//443// ## User control levels444//445// chain-controlled - key is uncontrolled by user446//                    i.e autoincrementing index447//                    can use non-cryptographic hash448// real - key is controlled by user449//        but it is hard to generate enough colliding values, i.e owner of signed txs450//        can use non-cryptographic hash451// controlled - key is completly controlled by users452//              i.e maps with mutable keys453//              should use cryptographic hash454//455// ## User control level downgrade reasons456//457// ?1 - chain-controlled -> controlled458//      collections/tokens can be destroyed, resulting in massive holes459// ?2 - chain-controlled -> controlled460//      same as ?1, but can be only added, resulting in easier exploitation461// ?3 - real -> controlled462//      no confirmation required, so addresses can be easily generated463decl_storage! {464    trait Store for Module<T: Config> as Nft {465466        //#region Private members467        /// Id of next collection468        CreatedCollectionCount: u32;469        /// Used for migrations470        ChainVersion: u64;471        /// Id of last collection token472        /// Collection id (controlled?1)473        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;474        //#endregion475476        //#region Chain limits struct477        pub ChainLimit get(fn chain_limit) config(): ChainLimits;478        //#endregion479480        //#region Bound counters481        /// Amount of collections destroyed, used for total amount tracking with482        /// CreatedCollectionCount483        DestroyedCollectionCount: u32;484        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)485        /// Account id (real)486        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;487        //#endregion488489        //#region Basic collections490        /// Collection info491        /// Collection id (controlled?1)492        pub Collection get(fn collection) config(): map hasher(blake2_128_concat) CollectionId => CollectionType<T::AccountId>;493        /// List of collection admins494        /// Collection id (controlled?2)495        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;496        /// Whitelisted collection users497        /// Collection id (controlled?2), user id (controlled?3)498        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;499        //#endregion500501        /// How many of collection items user have502        /// Collection id (controlled?2), account id (real)503        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;504505        /// Amount of items which spender can transfer out of owners account (via transferFrom)506        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))507        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;508509        //#region Item collections510        /// Collection id (controlled?2), token id (controlled?1)511        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;512        /// Collection id (controlled?2), owner (controlled?2)513        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;514        /// Collection id (controlled?2), token id (controlled?1)515        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;516        //#endregion517518        //#region Index list519        /// Collection id (controlled?2), tokens owner (controlled?2)520        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;521        //#endregion522523        //#region Tokens transfer rate limit baskets524        /// (Collection id (controlled?2), who created (real))525        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;526        /// Collection id (controlled?2), token id (controlled?2)527        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;528        /// Collection id (controlled?2), owning user (real)529        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;530        /// Collection id (controlled?2), token id (controlled?2)531        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;532        //#endregion533534        //#region Contract Sponsorship and Ownership535        /// Contract address (real)536        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;537        /// Contract address (real)538        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;539        /// (Contract address(real), caller (real))540        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;541        /// Contract address (real)542        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;543        /// Contract address (real)544        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 545        /// Contract address (real) => Whitelisted user (controlled?3)546        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 547        //#endregion548    }549    add_extra_genesis {550        build(|config: &GenesisConfig<T>| {551            // Modification of storage552            for (_num, _c) in &config.collection {553                <Module<T>>::init_collection(_c);554            }555556            for (_num, _c, _i) in &config.nft_item_id {557                <Module<T>>::init_nft_token(*_c, _i);558            }559560            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {561                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);562            }563564            for (_num, _c, _i) in &config.refungible_item_id {565                <Module<T>>::init_refungible_token(*_c, _i);566            }567        })568    }569}570571decl_event!(572    pub enum Event<T>573    where574        AccountId = <T as system::Config>::AccountId,575    {576        /// New collection was created577        /// 578        /// # Arguments579        /// 580        /// * collection_id: Globally unique identifier of newly created collection.581        /// 582        /// * mode: [CollectionMode] converted into u8.583        /// 584        /// * account_id: Collection owner.585        Created(CollectionId, u8, AccountId),586587        /// New item was created.588        /// 589        /// # Arguments590        /// 591        /// * collection_id: Id of the collection where item was created.592        /// 593        /// * item_id: Id of an item. Unique within the collection.594        ///595        /// * recipient: Owner of newly created item 596        ItemCreated(CollectionId, TokenId, AccountId),597598        /// Collection item was burned.599        /// 600        /// # Arguments601        /// 602        /// collection_id.603        /// 604        /// item_id: Identifier of burned NFT.605        ItemDestroyed(CollectionId, TokenId),606607        /// Item was transferred608        ///609        /// * collection_id: Id of collection to which item is belong610        ///611        /// * item_id: Id of an item612        ///613        /// * sender: Original owner of item614        ///615        /// * recipient: New owner of item616        ///617        /// * amount: Always 1 for NFT618        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),619    }620);621622decl_module! {623    pub struct Module<T: Config> for enum Call 624    where 625        origin: T::Origin626    {627        fn deposit_event() = default;628        type Error = Error<T>;629630        fn on_initialize(now: T::BlockNumber) -> Weight {631            0632        }633634        /// 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.635        /// 636        /// # Permissions637        /// 638        /// * Anyone.639        /// 640        /// # Arguments641        /// 642        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.643        /// 644        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.645        /// 646        /// * token_prefix: UTF-8 string with token prefix.647        /// 648        /// * mode: [CollectionMode] collection type and type dependent data.649        // returns collection ID650        #[weight = <T as Config>::WeightInfo::create_collection()]651        pub fn create_collection(origin,652                                 collection_name: Vec<u16>,653                                 collection_description: Vec<u16>,654                                 token_prefix: Vec<u8>,655                                 mode: CollectionMode) -> DispatchResult {656657            // Anyone can create a collection658            let who = ensure_signed(origin)?;659660            let decimal_points = match mode {661                CollectionMode::Fungible(points) => points,662                _ => 0663            };664665            let chain_limit = ChainLimit::get();666667            let created_count = CreatedCollectionCount::get();668            let destroyed_count = DestroyedCollectionCount::get();669670            // bound Total number of collections671            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);672673            // check params674            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);675            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);676            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);677            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);678679            // Generate next collection ID680            let next_id = created_count681                .checked_add(1)682                .ok_or(Error::<T>::NumOverflow)?;683684            CreatedCollectionCount::put(next_id);685686            let limits = CollectionLimits {687                sponsored_data_size: chain_limit.custom_data_limit,688                ..Default::default()689            };690691            // Create new collection692            let new_collection = CollectionType {693                owner: who.clone(),694                name: collection_name,695                mode: mode.clone(),696                mint_mode: false,697                access: AccessMode::Normal,698                description: collection_description,699                decimal_points: decimal_points,700                token_prefix: token_prefix,701                offchain_schema: Vec::new(),702                schema_version: SchemaVersion::ImageURL,703                sponsorship: SponsorshipState::Disabled,704                variable_on_chain_schema: Vec::new(),705                const_on_chain_schema: Vec::new(),706                limits,707            };708709            // Add new collection to map710            <Collection<T>>::insert(next_id, new_collection);711712            // call event713            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));714715            Ok(())716        }717718        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.719        /// 720        /// # Permissions721        /// 722        /// * Collection Owner.723        /// 724        /// # Arguments725        /// 726        /// * collection_id: collection to destroy.727        #[weight = <T as Config>::WeightInfo::destroy_collection()]728        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {729730            let sender = ensure_signed(origin)?;731            Self::check_owner_permissions(collection_id, sender)?;732733            let target_collection = <Collection<T>>::get(collection_id);734            if !target_collection.limits.owner_can_destroy {735                fail!(Error::<T>::NoPermission);736            }737738            <AddressTokens<T>>::remove_prefix(collection_id);739            <Allowances<T>>::remove_prefix(collection_id);740            <Balance<T>>::remove_prefix(collection_id);741            <ItemListIndex>::remove(collection_id);742            <AdminList<T>>::remove(collection_id);743            <Collection<T>>::remove(collection_id);744            <WhiteList<T>>::remove_prefix(collection_id);745746            <NftItemList<T>>::remove_prefix(collection_id);747            <FungibleItemList<T>>::remove_prefix(collection_id);748            <ReFungibleItemList<T>>::remove_prefix(collection_id);749750            <NftTransferBasket<T>>::remove_prefix(collection_id);751            <FungibleTransferBasket<T>>::remove_prefix(collection_id);752            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);753754            DestroyedCollectionCount::put(DestroyedCollectionCount::get()755                .checked_add(1)756                .ok_or(Error::<T>::NumOverflow)?);757758            Ok(())759        }760761        /// Add an address to white list.762        /// 763        /// # Permissions764        /// 765        /// * Collection Owner766        /// * Collection Admin767        /// 768        /// # Arguments769        /// 770        /// * collection_id.771        /// 772        /// * address.773        #[weight = <T as Config>::WeightInfo::add_to_white_list()]774        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{775776            let sender = ensure_signed(origin)?;777            Self::check_owner_or_admin_permissions(collection_id, sender)?;778779            <WhiteList<T>>::insert(collection_id, address, true);780            781            Ok(())782        }783784        /// Remove an address from white list.785        /// 786        /// # Permissions787        /// 788        /// * Collection Owner789        /// * Collection Admin790        /// 791        /// # Arguments792        /// 793        /// * collection_id.794        /// 795        /// * address.796        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]797        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{798799            let sender = ensure_signed(origin)?;800            Self::check_owner_or_admin_permissions(collection_id, sender)?;801802            <WhiteList<T>>::remove(collection_id, address);803804            Ok(())805        }806807        /// Toggle between normal and white list access for the methods with access for `Anyone`.808        /// 809        /// # Permissions810        /// 811        /// * Collection Owner.812        /// 813        /// # Arguments814        /// 815        /// * collection_id.816        /// 817        /// * mode: [AccessMode]818        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]819        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult820        {821            let sender = ensure_signed(origin)?;822823            Self::check_owner_permissions(collection_id, sender)?;824            let mut target_collection = <Collection<T>>::get(collection_id);825            target_collection.access = mode;826            <Collection<T>>::insert(collection_id, target_collection);827828            Ok(())829        }830831        /// Allows Anyone to create tokens if:832        /// * White List is enabled, and833        /// * Address is added to white list, and834        /// * This method was called with True parameter835        /// 836        /// # Permissions837        /// * Collection Owner838        ///839        /// # Arguments840        /// 841        /// * collection_id.842        /// 843        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.844        #[weight = <T as Config>::WeightInfo::set_mint_permission()]845        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult846        {847            let sender = ensure_signed(origin)?;848849            Self::check_owner_permissions(collection_id, sender)?;850            let mut target_collection = <Collection<T>>::get(collection_id);851            target_collection.mint_mode = mint_permission;852            <Collection<T>>::insert(collection_id, target_collection);853854            Ok(())855        }856857        /// Change the owner of the collection.858        /// 859        /// # Permissions860        /// 861        /// * Collection Owner.862        /// 863        /// # Arguments864        /// 865        /// * collection_id.866        /// 867        /// * new_owner.868        #[weight = <T as Config>::WeightInfo::change_collection_owner()]869        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {870871            let sender = ensure_signed(origin)?;872            Self::check_owner_permissions(collection_id, sender)?;873            let mut target_collection = <Collection<T>>::get(collection_id);874            target_collection.owner = new_owner;875            <Collection<T>>::insert(collection_id, target_collection);876877            Ok(())878        }879880        /// Adds an admin of the Collection.881        /// 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. 882        /// 883        /// # Permissions884        /// 885        /// * Collection Owner.886        /// * Collection Admin.887        /// 888        /// # Arguments889        /// 890        /// * collection_id: ID of the Collection to add admin for.891        /// 892        /// * new_admin_id: Address of new admin to add.893        #[weight = <T as Config>::WeightInfo::add_collection_admin()]894        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {895896            let sender = ensure_signed(origin)?;897            Self::check_owner_or_admin_permissions(collection_id, sender)?;898            let mut admin_arr: Vec<T::AccountId> = Vec::new();899900            if <AdminList<T>>::contains_key(collection_id)901            {902                admin_arr = <AdminList<T>>::get(collection_id);903                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);904            }905906            // Number of collection admins907            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);908909            admin_arr.push(new_admin_id);910            <AdminList<T>>::insert(collection_id, admin_arr);911912            Ok(())913        }914915        /// 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.916        ///917        /// # Permissions918        /// 919        /// * Collection Owner.920        /// * Collection Admin.921        /// 922        /// # Arguments923        /// 924        /// * collection_id: ID of the Collection to remove admin for.925        /// 926        /// * account_id: Address of admin to remove.927        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]928        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {929930            let sender = ensure_signed(origin)?;931            Self::check_owner_or_admin_permissions(collection_id, sender)?;932            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);933934            let mut admin_arr = <AdminList<T>>::get(collection_id);935            admin_arr.retain(|i| *i != account_id);936            <AdminList<T>>::insert(collection_id, admin_arr);937938            Ok(())939        }940941        /// # Permissions942        /// 943        /// * Collection Owner944        /// 945        /// # Arguments946        /// 947        /// * collection_id.948        /// 949        /// * new_sponsor.950        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]951        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {952953            let sender = ensure_signed(origin)?;954            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);955956            let mut target_collection = <Collection<T>>::get(collection_id);957            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);958959            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);960            <Collection<T>>::insert(collection_id, target_collection);961962            Ok(())963        }964965        /// # Permissions966        /// 967        /// * Sponsor.968        /// 969        /// # Arguments970        /// 971        /// * collection_id.972        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]973        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {974975            let sender = ensure_signed(origin)?;976            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);977978            let mut target_collection = <Collection<T>>::get(collection_id);979            ensure!(980                target_collection.sponsorship.pending_sponsor() == Some(&sender),981                Error::<T>::ConfirmUnsetSponsorFail982            );983984            target_collection.sponsorship = SponsorshipState::Confirmed(sender);985            <Collection<T>>::insert(collection_id, target_collection);986987            Ok(())988        }989990        /// Switch back to pay-per-own-transaction model.991        ///992        /// # Permissions993        ///994        /// * Collection owner.995        /// 996        /// # Arguments997        /// 998        /// * collection_id.999        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1000        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10011002            let sender = ensure_signed(origin)?;1003            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);10041005            let mut target_collection = <Collection<T>>::get(collection_id);1006            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);10071008            target_collection.sponsorship = SponsorshipState::Disabled;1009            <Collection<T>>::insert(collection_id, target_collection);10101011            Ok(())1012        }10131014        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1015        /// 1016        /// # Permissions1017        /// 1018        /// * Collection Owner.1019        /// * Collection Admin.1020        /// * Anyone if1021        ///     * White List is enabled, and1022        ///     * Address is added to white list, and1023        ///     * MintPermission is enabled (see SetMintPermission method)1024        /// 1025        /// # Arguments1026        /// 1027        /// * collection_id: ID of the collection.1028        /// 1029        /// * owner: Address, initial owner of the NFT.1030        ///1031        /// * data: Token data to store on chain.1032        // #[weight =1033        // (130_000_000 as Weight)1034        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1035        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1036        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]10371038        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1039        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {10401041            let sender = ensure_signed(origin)?;10421043            Self::collection_exists(collection_id)?;10441045            let target_collection = <Collection<T>>::get(collection_id);10461047            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;1048            Self::validate_create_item_args(&target_collection, &data)?;1049            Self::create_item_no_validation(collection_id, owner, data)?;10501051            Ok(())1052        }10531054        /// This method creates multiple instances of NFT Collection created with CreateCollection method.1055        /// 1056        /// # Permissions1057        /// 1058        /// * Collection Owner.1059        /// * Collection Admin.1060        /// * Anyone if1061        ///     * White List is enabled, and1062        ///     * Address is added to white list, and1063        ///     * MintPermission is enabled (see SetMintPermission method)1064        /// 1065        /// # Arguments1066        /// 1067        /// * collection_id: ID of the collection.1068        /// 1069        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1070        /// 1071        /// * owner: Address, initial owner of the NFT.1072        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1073                               .map(|data| { data.len() })1074                               .sum())]1075        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10761077            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1078            let sender = ensure_signed(origin)?;10791080            Self::collection_exists(collection_id)?;1081            let target_collection = <Collection<T>>::get(collection_id);10821083            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10841085            for data in &items_data {1086                Self::validate_create_item_args(&target_collection, data)?;1087            }1088            for data in &items_data {1089                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1090            }10911092            Ok(())1093        }10941095        /// Destroys a concrete instance of NFT.1096        /// 1097        /// # Permissions1098        /// 1099        /// * Collection Owner.1100        /// * Collection Admin.1101        /// * Current NFT Owner.1102        /// 1103        /// # Arguments1104        /// 1105        /// * collection_id: ID of the collection.1106        /// 1107        /// * item_id: ID of NFT to burn.1108        #[weight = <T as Config>::WeightInfo::burn_item()]1109        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11101111            let sender = ensure_signed(origin)?;1112            Self::collection_exists(collection_id)?;11131114            // Transfer permissions check1115            let target_collection = <Collection<T>>::get(collection_id);1116            ensure!(1117                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1118                (1119                    target_collection.limits.owner_can_transfer &&1120                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1121                ),1122                Error::<T>::NoPermission1123            );11241125            if target_collection.access == AccessMode::WhiteList {1126                Self::check_white_list(collection_id, &sender)?;1127            }11281129            match target_collection.mode1130            {1131                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1132                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1133                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1134                _ => ()1135            };11361137            // call event1138            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));11391140            Ok(())1141        }11421143        /// Change ownership of the token.1144        /// 1145        /// # Permissions1146        /// 1147        /// * Collection Owner1148        /// * Collection Admin1149        /// * Current NFT owner1150        ///1151        /// # Arguments1152        /// 1153        /// * recipient: Address of token recipient.1154        /// 1155        /// * collection_id.1156        /// 1157        /// * item_id: ID of the item1158        ///     * Non-Fungible Mode: Required.1159        ///     * Fungible Mode: Ignored.1160        ///     * Re-Fungible Mode: Required.1161        /// 1162        /// * value: Amount to transfer.1163        ///     * Non-Fungible Mode: Ignored1164        ///     * Fungible Mode: Must specify transferred amount1165        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1166        #[weight = <T as Config>::WeightInfo::transfer()]1167        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1168            let sender = ensure_signed(origin)?;1169            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1170        }11711172        /// Set, change, or remove approved address to transfer the ownership of the NFT.1173        /// 1174        /// # Permissions1175        /// 1176        /// * Collection Owner1177        /// * Collection Admin1178        /// * Current NFT owner1179        /// 1180        /// # Arguments1181        /// 1182        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1183        /// 1184        /// * collection_id.1185        /// 1186        /// * item_id: ID of the item.1187        #[weight = <T as Config>::WeightInfo::approve()]1188        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11891190            let sender = ensure_signed(origin)?;11911192            Self::collection_exists(collection_id)?;1193            Self::token_exists(collection_id, item_id, &sender)?;11941195            // Transfer permissions check1196            let target_collection = <Collection<T>>::get(collection_id);1197            let allowance_limit = if target_collection.limits.owner_can_transfer &&1198                Self::is_owner_or_admin_permissions(1199                    collection_id,1200                    sender.clone(),1201                ) {1202                None1203            } else if let Some(amount) = Self::owned_amount(1204                sender.clone(),1205                collection_id,1206                item_id,1207            ) {1208                Some(amount)1209            } else {1210                fail!(Error::<T>::NoPermission);1211            };12121213            if target_collection.access == AccessMode::WhiteList {1214                Self::check_white_list(collection_id, &sender)?;1215                Self::check_white_list(collection_id, &spender)?;1216            }12171218            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1219            let mut allowance: u128 = amount;1220            if allowance_exists {1221                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1222            }1223            if let Some(limit) = allowance_limit {1224                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1225            }1226            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12271228            Ok(())1229        }1230        1231        /// 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.1232        /// 1233        /// # Permissions1234        /// * Collection Owner1235        /// * Collection Admin1236        /// * Current NFT owner1237        /// * Address approved by current NFT owner1238        /// 1239        /// # Arguments1240        /// 1241        /// * from: Address that owns token.1242        /// 1243        /// * recipient: Address of token recipient.1244        /// 1245        /// * collection_id.1246        /// 1247        /// * item_id: ID of the item.1248        /// 1249        /// * value: Amount to transfer.1250        #[weight = <T as Config>::WeightInfo::transfer_from()]1251        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12521253            let sender = ensure_signed(origin)?;1254            let mut appoved_transfer = false;12551256            // Check approval1257            let mut approval: u128 = 0;1258            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1259                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1260                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1261                appoved_transfer = true;1262            }12631264            let target_collection = <Collection<T>>::get(collection_id);12651266            // Limits check1267            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12681269            // Transfer permissions check         1270            ensure!(1271                appoved_transfer || 1272                (1273                    target_collection.limits.owner_can_transfer &&1274                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1275                ),1276                Error::<T>::NoPermission1277            );12781279            if target_collection.access == AccessMode::WhiteList {1280                Self::check_white_list(collection_id, &sender)?;1281                Self::check_white_list(collection_id, &recipient)?;1282            }12831284            // Reduce approval by transferred amount or remove if remaining approval drops to 01285            if approval.checked_sub(value).unwrap_or(0) > 0 {1286                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1287            }1288            else {1289                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1290            }12911292            match target_collection.mode1293            {1294                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1295                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1296                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1297                _ => ()1298            };12991300            Ok(())1301        }13021303        // #[weight = 0]1304        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13051306        //     // let no_perm_mes = "You do not have permissions to modify this collection";1307        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1308        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1309        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13101311        //     // // on_nft_received  call13121313        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13141315        //     Ok(())1316        // }13171318        /// Set off-chain data schema.1319        /// 1320        /// # Permissions1321        /// 1322        /// * Collection Owner1323        /// * Collection Admin1324        /// 1325        /// # Arguments1326        /// 1327        /// * collection_id.1328        /// 1329        /// * schema: String representing the offchain data schema.1330        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1331        pub fn set_variable_meta_data (1332            origin,1333            collection_id: CollectionId,1334            item_id: TokenId,1335            data: Vec<u8>1336        ) -> DispatchResult {1337            let sender = ensure_signed(origin)?;1338            1339            Self::collection_exists(collection_id)?;1340            Self::token_exists(collection_id, item_id, &sender)?;13411342            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13431344            // Modify permissions check1345            let target_collection = <Collection<T>>::get(collection_id);1346            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1347                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1348                Error::<T>::NoPermission);13491350            match target_collection.mode1351            {1352                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1353                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1354                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1355                _ => fail!(Error::<T>::UnexpectedCollectionType)1356            };13571358            Ok(())1359        }1360 1361        /// Set schema standard1362        /// ImageURL1363        /// Unique1364        /// 1365        /// # Permissions1366        /// 1367        /// * Collection Owner1368        /// * Collection Admin1369        /// 1370        /// # Arguments1371        /// 1372        /// * collection_id.1373        /// 1374        /// * schema: SchemaVersion: enum1375        #[weight = <T as Config>::WeightInfo::set_schema_version()]1376        pub fn set_schema_version(1377            origin,1378            collection_id: CollectionId,1379            version: SchemaVersion1380        ) -> DispatchResult {1381            let sender = ensure_signed(origin)?;1382            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1383            let mut target_collection = <Collection<T>>::get(collection_id);1384            target_collection.schema_version = version;1385            <Collection<T>>::insert(collection_id, target_collection);13861387            Ok(())1388        }13891390        /// Set off-chain data schema.1391        /// 1392        /// # Permissions1393        /// 1394        /// * Collection Owner1395        /// * Collection Admin1396        /// 1397        /// # Arguments1398        /// 1399        /// * collection_id.1400        /// 1401        /// * schema: String representing the offchain data schema.1402        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1403        pub fn set_offchain_schema(1404            origin,1405            collection_id: CollectionId,1406            schema: Vec<u8>1407        ) -> DispatchResult {1408            let sender = ensure_signed(origin)?;1409            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14101411            // check schema limit1412            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14131414            let mut target_collection = <Collection<T>>::get(collection_id);1415            target_collection.offchain_schema = schema;1416            <Collection<T>>::insert(collection_id, target_collection);14171418            Ok(())1419        }14201421        /// Set const on-chain data schema.1422        /// 1423        /// # Permissions1424        /// 1425        /// * Collection Owner1426        /// * Collection Admin1427        /// 1428        /// # Arguments1429        /// 1430        /// * collection_id.1431        /// 1432        /// * schema: String representing the const on-chain data schema.1433        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1434        pub fn set_const_on_chain_schema (1435            origin,1436            collection_id: CollectionId,1437            schema: Vec<u8>1438        ) -> DispatchResult {1439            let sender = ensure_signed(origin)?;1440            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14411442            // check schema limit1443            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14441445            let mut target_collection = <Collection<T>>::get(collection_id);1446            target_collection.const_on_chain_schema = schema;1447            <Collection<T>>::insert(collection_id, target_collection);14481449            Ok(())1450        }14511452        /// Set variable on-chain data schema.1453        /// 1454        /// # Permissions1455        /// 1456        /// * Collection Owner1457        /// * Collection Admin1458        /// 1459        /// # Arguments1460        /// 1461        /// * collection_id.1462        /// 1463        /// * schema: String representing the variable on-chain data schema.1464        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1465        pub fn set_variable_on_chain_schema (1466            origin,1467            collection_id: CollectionId,1468            schema: Vec<u8>1469        ) -> DispatchResult {1470            let sender = ensure_signed(origin)?;1471            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14721473            // check schema limit1474            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14751476            let mut target_collection = <Collection<T>>::get(collection_id);1477            target_collection.variable_on_chain_schema = schema;1478            <Collection<T>>::insert(collection_id, target_collection);14791480            Ok(())1481        }14821483        // Sudo permissions function1484        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1485        pub fn set_chain_limits(1486            origin,1487            limits: ChainLimits1488        ) -> DispatchResult {14891490            #[cfg(not(feature = "runtime-benchmarks"))]1491            ensure_root(origin)?;14921493            <ChainLimit>::put(limits);1494            Ok(())1495        }14961497        /// Enable smart contract self-sponsoring.1498        /// 1499        /// # Permissions1500        /// 1501        /// * Contract Owner1502        /// 1503        /// # Arguments1504        /// 1505        /// * contract address1506        /// * enable flag1507        /// 1508        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1509        pub fn enable_contract_sponsoring(1510            origin,1511            contract_address: T::AccountId,1512            enable: bool1513        ) -> DispatchResult {15141515            let sender = ensure_signed(origin)?;15161517            #[cfg(feature = "runtime-benchmarks")]1518            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15191520            Self::ensure_contract_owned(sender, &contract_address)?;15211522            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1523            Ok(())1524        }15251526        /// Set the rate limit for contract sponsoring to specified number of blocks.1527        /// 1528        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1529        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1530        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1531        /// from contract endowment if there are at least B blocks between such transactions. 1532        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1533        /// 1534        /// # Permissions1535        /// 1536        /// * Contract Owner1537        /// 1538        /// # Arguments1539        /// 1540        /// -`contract_address`: Address of the contract to sponsor1541        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1542        /// 1543        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1544        pub fn set_contract_sponsoring_rate_limit(1545            origin,1546            contract_address: T::AccountId,1547            rate_limit: T::BlockNumber1548        ) -> DispatchResult {1549            let sender = ensure_signed(origin)?;15501551            #[cfg(feature = "runtime-benchmarks")]1552            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15531554            Self::ensure_contract_owned(sender, &contract_address)?;1555            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1556            Ok(())1557        }15581559        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1560        /// 1561        /// # Permissions1562        /// 1563        /// * Address that deployed smart contract.1564        /// 1565        /// # Arguments1566        /// 1567        /// -`contract_address`: Address of the contract.1568        /// 1569        /// - `enable`: .  1570        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1571        pub fn toggle_contract_white_list(1572            origin,1573            contract_address: T::AccountId,1574            enable: bool1575        ) -> DispatchResult {1576            let sender = ensure_signed(origin)?;15771578            #[cfg(feature = "runtime-benchmarks")]1579            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15801581            Self::ensure_contract_owned(sender, &contract_address)?;1582            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1583            Ok(())1584        }1585        1586        /// Add an address to smart contract white list.1587        /// 1588        /// # Permissions1589        /// 1590        /// * Address that deployed smart contract.1591        /// 1592        /// # Arguments1593        /// 1594        /// -`contract_address`: Address of the contract.1595        ///1596        /// -`account_address`: Address to add.1597        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1598        pub fn add_to_contract_white_list(1599            origin,1600            contract_address: T::AccountId,1601            account_address: T::AccountId1602        ) -> DispatchResult {1603            let sender = ensure_signed(origin)?;16041605            #[cfg(feature = "runtime-benchmarks")]1606            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1607            1608            Self::ensure_contract_owned(sender, &contract_address)?;      1609            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1610            Ok(())1611        }16121613        /// Remove an address from smart contract white list.1614        /// 1615        /// # Permissions1616        /// 1617        /// * Address that deployed smart contract.1618        /// 1619        /// # Arguments1620        /// 1621        /// -`contract_address`: Address of the contract.1622        ///1623        /// -`account_address`: Address to remove.1624        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1625        pub fn remove_from_contract_white_list(1626            origin,1627            contract_address: T::AccountId,1628            account_address: T::AccountId1629        ) -> DispatchResult {1630            let sender = ensure_signed(origin)?;16311632            #[cfg(feature = "runtime-benchmarks")]1633            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16341635            Self::ensure_contract_owned(sender, &contract_address)?;1636            <ContractWhiteList<T>>::remove(contract_address, account_address);1637            Ok(())1638        }16391640        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1641        pub fn set_collection_limits(1642            origin,1643            collection_id: u32,1644            new_limits: CollectionLimits,1645        ) -> DispatchResult {1646            let sender = ensure_signed(origin)?;1647            Self::check_owner_permissions(collection_id, sender.clone())?;1648            let mut target_collection = <Collection<T>>::get(collection_id);1649            let old_limits = target_collection.limits;1650            let chain_limits = ChainLimit::get();16511652            // collection bounds1653            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1654                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1655                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1656                Error::<T>::CollectionLimitBoundsExceeded);16571658            // token_limit   check  prev1659            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1660            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16611662            ensure!(1663                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1664                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1665                Error::<T>::OwnerPermissionsCantBeReverted,1666            );16671668            target_collection.limits = new_limits;1669            <Collection<T>>::insert(collection_id, target_collection);16701671            Ok(())1672        } 1673    }1674}16751676impl<T: Config> Module<T> {16771678    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {16791680        let target_collection = <Collection<T>>::get(collection_id);16811682        // Limits check1683        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16841685        // Transfer permissions check1686        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1687            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1688            Error::<T>::NoPermission);16891690        if target_collection.access == AccessMode::WhiteList {1691            Self::check_white_list(collection_id, &sender)?;1692            Self::check_white_list(collection_id, &recipient)?;1693        }16941695        match target_collection.mode1696        {1697            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1698            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1699            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1700            _ => ()1701        };17021703        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));17041705        Ok(())1706    }170717081709    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {17101711        // check token limit and account token limit1712        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1713        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1714        1715        Ok(())1716    }17171718    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {17191720        // check token limit and account token limit1721        let total_items: u32 = ItemListIndex::get(collection_id);1722        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1723        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1724        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);17251726        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1727            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1728            Self::check_white_list(collection_id, owner)?;1729            Self::check_white_list(collection_id, sender)?;1730        }17311732        Ok(())1733    }17341735    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1736        match target_collection.mode1737        {1738            CollectionMode::NFT => {1739                if let CreateItemData::NFT(data) = data {1740                    // check sizes1741                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1742                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1743                } else {1744                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1745                }1746            },1747            CollectionMode::Fungible(_) => {1748                if let CreateItemData::Fungible(_) = data {1749                } else {1750                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1751                }1752            },1753            CollectionMode::ReFungible => {1754                if let CreateItemData::ReFungible(data) = data {17551756                    // check sizes1757                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1758                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17591760                    // Check refungibility limits1761                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1762                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1763                } else {1764                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1765                }1766            },1767            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1768        };17691770        Ok(())1771    }17721773    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1774        match data1775        {1776            CreateItemData::NFT(data) => {1777                let item = NftItemType {1778                    owner: owner.clone(),1779                    const_data: data.const_data,1780                    variable_data: data.variable_data1781                };17821783                Self::add_nft_item(collection_id, item)?;1784            },1785            CreateItemData::Fungible(data) => {1786                Self::add_fungible_item(collection_id, &owner, data.value)?;1787            },1788            CreateItemData::ReFungible(data) => {1789                let mut owner_list = Vec::new();1790                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17911792                let item = ReFungibleItemType {1793                    owner: owner_list,1794                    const_data: data.const_data,1795                    variable_data: data.variable_data1796                };17971798                Self::add_refungible_item(collection_id, item)?;1799            }1800        };18011802        // call event1803        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));18041805        Ok(())1806    }18071808    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {18091810        // Does new owner already have an account?1811        let mut balance: u128 = 0;1812        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1813            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1814        } 18151816        // Mint 1817        let item = FungibleItemType {1818            value: balance + value1819        };1820        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);18211822        // Update balance1823        let new_balance = <Balance<T>>::get(collection_id, owner)1824            .checked_add(value)1825            .ok_or(Error::<T>::NumOverflow)?;1826        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18271828        Ok(())1829    }18301831    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1832        let current_index = <ItemListIndex>::get(collection_id)1833            .checked_add(1)1834            .ok_or(Error::<T>::NumOverflow)?;1835        let itemcopy = item.clone();18361837        let value = item.owner.first().unwrap().fraction;1838        let owner = item.owner.first().unwrap().owner.clone();18391840        Self::add_token_index(collection_id, current_index, &owner)?;18411842        <ItemListIndex>::insert(collection_id, current_index);1843        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18441845        // Update balance1846        let new_balance = <Balance<T>>::get(collection_id, &owner)1847            .checked_add(value)1848            .ok_or(Error::<T>::NumOverflow)?;1849        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);18501851        Ok(())1852    }18531854    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1855        let current_index = <ItemListIndex>::get(collection_id)1856            .checked_add(1)1857            .ok_or(Error::<T>::NumOverflow)?;18581859        let item_owner = item.owner.clone();1860        Self::add_token_index(collection_id, current_index, &item.owner)?;18611862        <ItemListIndex>::insert(collection_id, current_index);1863        <NftItemList<T>>::insert(collection_id, current_index, item);18641865        // Update balance1866        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1867            .checked_add(1)1868            .ok_or(Error::<T>::NumOverflow)?;1869        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18701871        Ok(())1872    }18731874    fn burn_refungible_item(1875        collection_id: CollectionId,1876        item_id: TokenId,1877        owner: &T::AccountId,1878    ) -> DispatchResult {1879        ensure!(1880            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1881            Error::<T>::TokenNotFound1882        );1883        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1884        let rft_balance = token1885            .owner1886            .iter()1887            .filter(|&i| i.owner == *owner)1888            .next()1889            .unwrap();1890        Self::remove_token_index(collection_id, item_id, owner)?;18911892        // update balance1893        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1894            .checked_sub(rft_balance.fraction)1895            .ok_or(Error::<T>::NumOverflow)?;1896        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18971898        // Re-create owners list with sender removed1899        let index = token1900            .owner1901            .iter()1902            .position(|i| i.owner == *owner)1903            .unwrap();1904        token.owner.remove(index);1905        let owner_count = token.owner.len();19061907        // Burn the token completely if this was the last (only) owner1908        if owner_count == 0 {1909            <ReFungibleItemList<T>>::remove(collection_id, item_id);1910        }1911        else {1912            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1913        }19141915        Ok(())1916    }19171918    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1919        ensure!(1920            <NftItemList<T>>::contains_key(collection_id, item_id),1921            Error::<T>::TokenNotFound1922        );1923        let item = <NftItemList<T>>::get(collection_id, item_id);1924        Self::remove_token_index(collection_id, item_id, &item.owner)?;19251926        // update balance1927        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1928            .checked_sub(1)1929            .ok_or(Error::<T>::NumOverflow)?;1930        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1931        <NftItemList<T>>::remove(collection_id, item_id);19321933        Ok(())1934    }19351936    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1937        ensure!(1938            <FungibleItemList<T>>::contains_key(collection_id, owner),1939            Error::<T>::TokenNotFound1940        );1941        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1942        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19431944        // update balance1945        let new_balance = <Balance<T>>::get(collection_id, owner)1946            .checked_sub(value)1947            .ok_or(Error::<T>::NumOverflow)?;1948        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19491950        if balance.value - value > 0 {1951            balance.value -= value;1952            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1953        }1954        else {1955            <FungibleItemList<T>>::remove(collection_id, owner);1956        }19571958        Ok(())1959    }19601961    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1962        ensure!(1963            <Collection<T>>::contains_key(collection_id),1964            Error::<T>::CollectionNotFound1965        );1966        Ok(())1967    }19681969    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1970        Self::collection_exists(collection_id)?;19711972        let target_collection = <Collection<T>>::get(collection_id);1973        ensure!(1974            subject == target_collection.owner,1975            Error::<T>::NoPermission1976        );19771978        Ok(())1979    }19801981    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1982        let target_collection = <Collection<T>>::get(collection_id);1983        let mut result: bool = subject == target_collection.owner;1984        let exists = <AdminList<T>>::contains_key(collection_id);19851986        if !result & exists {1987            if <AdminList<T>>::get(collection_id).contains(&subject) {1988                result = true1989            }1990        }19911992        result1993    }19941995    fn check_owner_or_admin_permissions(1996        collection_id: CollectionId,1997        subject: T::AccountId,1998    ) -> DispatchResult {1999        Self::collection_exists(collection_id)?;2000        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());20012002        ensure!(2003            result,2004            Error::<T>::NoPermission2005        );2006        Ok(())2007    }20082009    fn owned_amount(2010        subject: T::AccountId,2011        collection_id: CollectionId,2012        item_id: TokenId,2013    ) -> Option<u128> {2014        let target_collection = <Collection<T>>::get(collection_id);20152016        match target_collection.mode {2017            CollectionMode::NFT => {2018                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {2019                    return Some(1)2020                }2021                None2022            },2023            CollectionMode::Fungible(_) => {2024                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {2025                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)2026                        .value);2027                }2028                None2029            },2030            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)2031                .owner2032                .iter()2033                .find(|i| i.owner == subject)2034                .map(|i| i.fraction),2035            CollectionMode::Invalid => None,2036        }2037    }20382039    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {2040        let target_collection = <Collection<T>>::get(collection_id);20412042        match target_collection.mode {2043            CollectionMode::NFT => {2044                <NftItemList<T>>::get(collection_id, item_id).owner == subject2045            }2046            CollectionMode::Fungible(_) => {2047                <FungibleItemList<T>>::contains_key(collection_id, &subject)2048            }2049            CollectionMode::ReFungible => {2050                <ReFungibleItemList<T>>::get(collection_id, item_id)2051                    .owner2052                    .iter()2053                    .any(|i| i.owner == subject)2054            }2055            CollectionMode::Invalid => false,2056        }2057    }20582059    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {2060        let mes = Error::<T>::AddresNotInWhiteList;2061        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20622063        Ok(())2064    }20652066    /// Check if token exists. In case of Fungible, check if there is an entry for 2067    /// the owner in fungible balances double map2068    fn token_exists(2069        collection_id: CollectionId,2070        item_id: TokenId,2071        owner: &T::AccountId2072    ) -> DispatchResult {2073        let target_collection = <Collection<T>>::get(collection_id);2074        let exists = match target_collection.mode2075        {2076            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2077            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2078            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2079            _ => false2080        };20812082        ensure!(exists == true, Error::<T>::TokenNotFound);2083        Ok(())2084    }20852086    fn transfer_fungible(2087        collection_id: CollectionId,2088        value: u128,2089        owner: &T::AccountId,2090        recipient: &T::AccountId,2091    ) -> DispatchResult {2092        Self::token_exists(collection_id, 0, owner)?;20932094        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2095        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20962097        // Send balance to recipient (updates balanceOf of recipient)2098        Self::add_fungible_item(collection_id, recipient, value)?;20992100        // update balanceOf of sender2101        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);21022103        // Reduce or remove sender2104        if balance.value == value {2105            <FungibleItemList<T>>::remove(collection_id, owner);2106        }2107        else {2108            balance.value -= value;2109            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2110        }21112112        Ok(())2113    }21142115    fn transfer_refungible(2116        collection_id: CollectionId,2117        item_id: TokenId,2118        value: u128,2119        owner: T::AccountId,2120        new_owner: T::AccountId,2121    ) -> DispatchResult {2122        Self::token_exists(collection_id, item_id, &owner)?;21232124        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2125        let item = full_item2126            .owner2127            .iter()2128            .filter(|i| i.owner == owner)2129            .next()2130            .ok_or(Error::<T>::NumOverflow)?;2131        let amount = item.fraction;21322133        ensure!(amount >= value, Error::<T>::TokenValueTooLow);21342135        // update balance2136        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2137            .checked_sub(value)2138            .ok_or(Error::<T>::NumOverflow)?;2139        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21402141        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2142            .checked_add(value)2143            .ok_or(Error::<T>::NumOverflow)?;2144        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21452146        let old_owner = item.owner.clone();2147        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21482149        // transfer2150        if amount == value && !new_owner_has_account {2151            // change owner2152            // new owner do not have account2153            let mut new_full_item = full_item.clone();2154            new_full_item2155                .owner2156                .iter_mut()2157                .find(|i| i.owner == owner)2158                .unwrap()2159                .owner = new_owner.clone();2160            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21612162            // update index collection2163            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2164        } else {2165            let mut new_full_item = full_item.clone();2166            new_full_item2167                .owner2168                .iter_mut()2169                .find(|i| i.owner == owner)2170                .unwrap()2171                .fraction -= value;21722173            // separate amount2174            if new_owner_has_account {2175                // new owner has account2176                new_full_item2177                    .owner2178                    .iter_mut()2179                    .find(|i| i.owner == new_owner)2180                    .unwrap()2181                    .fraction += value;2182            } else {2183                // new owner do not have account2184                new_full_item.owner.push(Ownership {2185                    owner: new_owner.clone(),2186                    fraction: value,2187                });2188                Self::add_token_index(collection_id, item_id, &new_owner)?;2189            }21902191            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2192        }21932194        Ok(())2195    }21962197    fn transfer_nft(2198        collection_id: CollectionId,2199        item_id: TokenId,2200        sender: T::AccountId,2201        new_owner: T::AccountId,2202    ) -> DispatchResult {2203        Self::token_exists(collection_id, item_id, &sender)?;22042205        let mut item = <NftItemList<T>>::get(collection_id, item_id);22062207        ensure!(2208            sender == item.owner,2209            Error::<T>::MustBeTokenOwner2210        );22112212        // update balance2213        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2214            .checked_sub(1)2215            .ok_or(Error::<T>::NumOverflow)?;2216        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22172218        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2219            .checked_add(1)2220            .ok_or(Error::<T>::NumOverflow)?;2221        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);22222223        // change owner2224        let old_owner = item.owner.clone();2225        item.owner = new_owner.clone();2226        <NftItemList<T>>::insert(collection_id, item_id, item);22272228        // update index collection2229        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22302231        Ok(())2232    }2233    2234    fn set_re_fungible_variable_data(2235        collection_id: CollectionId,2236        item_id: TokenId,2237        data: Vec<u8>2238    ) -> DispatchResult {2239        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);22402241        item.variable_data = data;22422243        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22442245        Ok(())2246    }22472248    fn set_nft_variable_data(2249        collection_id: CollectionId,2250        item_id: TokenId,2251        data: Vec<u8>2252    ) -> DispatchResult {2253        let mut item = <NftItemList<T>>::get(collection_id, item_id);2254        2255        item.variable_data = data;22562257        <NftItemList<T>>::insert(collection_id, item_id, item);2258        2259        Ok(())2260    }22612262    fn init_collection(item: &CollectionType<T::AccountId>) {2263        // check params2264        assert!(2265            item.decimal_points <= MAX_DECIMAL_POINTS,2266            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2267        );2268        assert!(2269            item.name.len() <= 64,2270            "Collection name can not be longer than 63 char"2271        );2272        assert!(2273            item.name.len() <= 256,2274            "Collection description can not be longer than 255 char"2275        );2276        assert!(2277            item.token_prefix.len() <= 16,2278            "Token prefix can not be longer than 15 char"2279        );22802281        // Generate next collection ID2282        let next_id = CreatedCollectionCount::get()2283            .checked_add(1)2284            .unwrap();22852286        CreatedCollectionCount::put(next_id);2287    }22882289    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2290        let current_index = <ItemListIndex>::get(collection_id)2291            .checked_add(1)2292            .unwrap();22932294        let item_owner = item.owner.clone();2295        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22962297        <ItemListIndex>::insert(collection_id, current_index);22982299        // Update balance2300        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2301            .checked_add(1)2302            .unwrap();2303        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2304    }23052306    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2307        let current_index = <ItemListIndex>::get(collection_id)2308            .checked_add(1)2309            .unwrap();23102311        Self::add_token_index(collection_id, current_index, owner).unwrap();23122313        <ItemListIndex>::insert(collection_id, current_index);23142315        // Update balance2316        let new_balance = <Balance<T>>::get(collection_id, owner)2317            .checked_add(item.value)2318            .unwrap();2319        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2320    }23212322    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2323        let current_index = <ItemListIndex>::get(collection_id)2324            .checked_add(1)2325            .unwrap();23262327        let value = item.owner.first().unwrap().fraction;2328        let owner = item.owner.first().unwrap().owner.clone();23292330        Self::add_token_index(collection_id, current_index, &owner).unwrap();23312332        <ItemListIndex>::insert(collection_id, current_index);23332334        // Update balance2335        let new_balance = <Balance<T>>::get(collection_id, &owner)2336            .checked_add(value)2337            .unwrap();2338        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2339    }23402341    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {23422343        // add to account limit2344        if <AccountItemCount<T>>::contains_key(owner) {23452346            // bound Owned tokens by a single address2347            let count = <AccountItemCount<T>>::get(owner);2348            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23492350            <AccountItemCount<T>>::insert(owner.clone(), count2351                .checked_add(1)2352                .ok_or(Error::<T>::NumOverflow)?);2353        }2354        else {2355            <AccountItemCount<T>>::insert(owner.clone(), 1);2356        }23572358        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2359        if list_exists {2360            let mut list = <AddressTokens<T>>::get(collection_id, owner);2361            let item_contains = list.contains(&item_index.clone());23622363            if !item_contains {2364                list.push(item_index.clone());2365            }23662367            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2368        } else {2369            let mut itm = Vec::new();2370            itm.push(item_index.clone());2371            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2372        }23732374        Ok(())2375    }23762377    fn remove_token_index(2378        collection_id: CollectionId,2379        item_index: TokenId,2380        owner: &T::AccountId,2381    ) -> DispatchResult {23822383        // update counter2384        <AccountItemCount<T>>::insert(owner.clone(), 2385            <AccountItemCount<T>>::get(owner)2386            .checked_sub(1)2387            .ok_or(Error::<T>::NumOverflow)?);238823892390        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2391        if list_exists {2392            let mut list = <AddressTokens<T>>::get(collection_id, owner);2393            let item_contains = list.contains(&item_index.clone());23942395            if item_contains {2396                list.retain(|&item| item != item_index);2397                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2398            }2399        }24002401        Ok(())2402    }24032404    fn move_token_index(2405        collection_id: CollectionId,2406        item_index: TokenId,2407        old_owner: &T::AccountId,2408        new_owner: &T::AccountId,2409    ) -> DispatchResult {2410        Self::remove_token_index(collection_id, item_index, old_owner)?;2411        Self::add_token_index(collection_id, item_index, new_owner)?;24122413        Ok(())2414    }2415    2416    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2417        if <ContractOwner<T>>::contains_key(contract.clone()) {2418            let owner = <ContractOwner<T>>::get(contract);2419            ensure!(account == owner, Error::<T>::NoPermission);2420        } else {2421            fail!(Error::<T>::NoPermission);2422        }24232424        Ok(())2425    }2426}24272428////////////////////////////////////////////////////////////////////////////////////////////////////2429// Economic models2430// #region24312432/// Fee multiplier.2433pub type Multiplier = FixedU128;24342435type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24362437/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2438/// in the queue.2439#[derive(Encode, Decode, Clone, Eq, PartialEq)]2440pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);24412442impl<T: Config + Send + Sync> sp_std::fmt::Debug 2443    for ChargeTransactionPayment<T>2444{2445	#[cfg(feature = "std")]2446	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2447		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2448	}2449	#[cfg(not(feature = "std"))]2450	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2451		Ok(())2452	}2453}24542455impl<T: Config> ChargeTransactionPayment<T>2456where2457    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2458    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2459    T::AccountId: AsRef<[u8]>,2460    T::AccountId: UncheckedFrom<T::Hash>,2461{2462    fn traditional_fee(2463        len: usize,2464        info: &DispatchInfoOf<T::Call>,2465        tip: BalanceOf<T>,2466    ) -> BalanceOf<T>2467    where2468        T::Call: Dispatchable<Info = DispatchInfo>,2469    {2470        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2471    }24722473	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2474        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2475        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2476        let len_saturation = max_block_length as u64 / (len as u64).max(1);2477        let coefficient: BalanceOf<T> = weight_saturation2478            .min(len_saturation)2479            .saturated_into::<BalanceOf<T>>();2480        final_fee2481            .saturating_mul(coefficient)2482            .saturated_into::<TransactionPriority>()2483    }24842485    fn withdraw_fee(2486        &self,2487        who: &T::AccountId,2488        call: &T::Call,2489        info: &DispatchInfoOf<T::Call>,2490        len: usize,2491	) -> Result<2492		(2493			BalanceOf<T>,2494			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2495		),2496		TransactionValidityError,2497	> {2498        let tip = self.0;24992500        // Set fee based on call type. Creating collection costs 1 Unique.2501        // All other transactions have traditional fees so far2502        // let fee = match call.is_sub_type() {2503        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2504        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2505        //                                                 // _ => <BalanceOf<T>>::from(100)2506        // };2507        let fee = Self::traditional_fee(len, info, tip);25082509        // Only mess with balances if fee is not zero.2510        if fee.is_zero() {2511            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2512			.map(|i| (fee, i));2513        }25142515        // Determine who is paying transaction fee based on ecnomic model2516        // Parse call to extract collection ID and access collection sponsor2517        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2518            Some(Call::create_item(collection_id, _owner, _properties)) => {25192520                // sponsor timeout2521                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25222523                let collection = <Collection<T>>::get(collection_id);25242525                let limit = collection.limits.sponsor_transfer_timeout;2526                let mut sponsored = true;2527                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2528                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2529                    let limit_time = last_tx_block + limit.into();2530                    if block_number <= limit_time {2531                        sponsored = false;2532                    }2533                }2534                if sponsored {2535                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2536                }25372538                // check free create limit2539                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2540                   (sponsored)2541                {2542                    collection.sponsorship.sponsor()2543                        .cloned()2544                        .unwrap_or_default()2545                } else {2546                    T::AccountId::default()2547                }2548            }2549            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2550                2551                let mut sponsor_transfer = false;2552                if <Collection<T>>::get(collection_id).sponsorship.confirmed() {25532554                    let collection_limits = <Collection<T>>::get(collection_id).limits;2555                    let collection_mode = <Collection<T>>::get(collection_id).mode;2556    2557                    // sponsor timeout2558                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2559                    sponsor_transfer = match collection_mode {2560                        CollectionMode::NFT => {2561    2562                            // get correct limit2563                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2564                                collection_limits.sponsor_transfer_timeout2565                            } else {2566                                ChainLimit::get().nft_sponsor_transfer_timeout2567                            };2568    2569                            let mut sponsored = true;2570                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2571                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2572                                let limit_time = last_tx_block + limit.into();2573                                if block_number <= limit_time {2574                                    sponsored = false;2575                                }2576                            }2577                            if sponsored {2578                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2579                            }25802581                            sponsored2582                        }2583                        CollectionMode::Fungible(_) => {2584    2585                            // get correct limit2586                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2587                                collection_limits.sponsor_transfer_timeout2588                            } else {2589                                ChainLimit::get().fungible_sponsor_transfer_timeout2590                            };2591    2592                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2593                            let mut sponsored = true;2594                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2595                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2596                                let limit_time = last_tx_block + limit.into();2597                                if block_number <= limit_time {2598                                    sponsored = false;2599                                }2600                            }2601                            if sponsored {2602                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2603                            }26042605                            sponsored2606                        }2607                        CollectionMode::ReFungible => {2608    2609                            // get correct limit2610                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2611                                collection_limits.sponsor_transfer_timeout2612                            } else {2613                                ChainLimit::get().refungible_sponsor_transfer_timeout2614                            };2615    2616                            let mut sponsored = true;2617                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2618                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2619                                let limit_time = last_tx_block + limit.into();2620                                if block_number <= limit_time {2621                                    sponsored = false;2622                                }2623                            }2624                            if sponsored {2625                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2626                            }26272628                            sponsored2629                        }2630                        _ => {2631                            false2632                        },2633                    };2634                }26352636                if !sponsor_transfer {2637                    T::AccountId::default()2638                } else {2639                    <Collection<T>>::get(collection_id).sponsorship.sponsor()2640                        .cloned()2641                        .unwrap_or_default()2642                }2643            }26442645            _ => T::AccountId::default(),2646        };26472648        // Sponsor smart contracts2649        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26502651            // On instantiation: set the contract owner2652            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26532654                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2655                    &who,2656                    code_hash,2657                    salt,2658                );2659                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26602661                T::AccountId::default()2662            },26632664            // On instantiation with code: set the contract owner2665            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {26662667                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2668                    &who,2669                    &T::Hashing::hash(&_code),2670                    _salt,2671                );26722673                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26742675                T::AccountId::default()2676            }26772678            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2679            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26802681                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26822683                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2684                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2685                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2686                  2687                if !owned_contract && white_list_enabled {2688                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2689                        return Err(InvalidTransaction::Call.into());2690                    }2691                }26922693                let mut sponsor_transfer = false;2694                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2695                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2696                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2697                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2698                    let limit_time = last_tx_block + rate_limit;26992700                    if block_number >= limit_time {2701                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2702                        sponsor_transfer = true;2703                    }2704                } else {2705                    sponsor_transfer = false;2706                }2707               2708                2709                let mut sp = T::AccountId::default();2710                if sponsor_transfer {2711                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2712                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2713                            sp = called_contract;2714                        }2715                    }2716                }27172718                sp2719            },27202721            _ => sponsor,2722        };27232724        let mut who_pays_fee: T::AccountId = sponsor.clone();2725        if sponsor == T::AccountId::default() {2726            who_pays_fee = who.clone();2727        }27282729		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2730			.map(|i| (fee, i))2731    }2732}273327342735impl<T: Config + Send + Sync> SignedExtension2736    for ChargeTransactionPayment<T>2737where2738    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2739    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2740    T::AccountId: AsRef<[u8]>,2741    T::AccountId: UncheckedFrom<T::Hash>,2742{2743    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2744    type AccountId = T::AccountId;2745    type Call = T::Call;2746    type AdditionalSigned = ();2747    type Pre = (2748        // tip2749        BalanceOf<T>,2750        // who pays fee2751        Self::AccountId,2752		// imbalance resulting from withdrawing the fee2753		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2754    );2755    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2756        Ok(())2757    }27582759    fn validate(2760        &self,2761        who: &Self::AccountId,2762        call: &Self::Call,2763        info: &DispatchInfoOf<Self::Call>,2764        len: usize,2765    ) -> TransactionValidity {2766		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2767		Ok(ValidTransaction {2768			priority: Self::get_priority(len, info, fee),2769			..Default::default()2770		})2771    }27722773    fn pre_dispatch(2774        self,2775        who: &Self::AccountId,2776        call: &Self::Call,2777        info: &DispatchInfoOf<Self::Call>,2778        len: usize,2779    ) -> Result<Self::Pre, TransactionValidityError> {2780        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2781        Ok((self.0, who.clone(), imbalance))2782    }27832784    fn post_dispatch(2785        pre: Self::Pre,2786        info: &DispatchInfoOf<Self::Call>,2787        post_info: &PostDispatchInfoOf<Self::Call>,2788        len: usize,2789        _result: &DispatchResult,2790    ) -> Result<(), TransactionValidityError> {2791		let (tip, who, imbalance) = pre;2792		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2793			len as u32,2794			info,2795			post_info,2796			tip,2797		);2798		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2799		Ok(())2800    }2801}28022803// #endregion
modifiedruntime_types.jsondiffbeforeafterboth
--- a/runtime_types.json
+++ b/runtime_types.json
@@ -31,6 +31,13 @@
       "ConstData": "Vec<u8>",
       "VariableData": "Vec<u8>"
     },
+    "SponsorshipState": {
+      "_enum": {
+        "Disabled": null,
+        "Unconfirmed": "AccountId",
+        "Confirmed": "AccountId"
+      }
+    },
     "CollectionType": {
       "Owner": "AccountId",
       "Mode": "CollectionMode",
@@ -42,8 +49,7 @@
       "MintMode": "bool",
       "OffchainSchema": "Vec<u8>",
       "SchemaVersion": "SchemaVersion",
-      "Sponsor": "AccountId",
-      "SponsorConfirmed": "bool",
+      "Sponsorship": "SponsorshipState",
       "Limits": "CollectionLimits",
       "VariableOnChainSchema": "Vec<u8>",
       "ConstOnChainSchema": "Vec<u8>"
modifiedtests/package.jsondiffbeforeafterboth
--- a/tests/package.json
+++ b/tests/package.json
@@ -26,6 +26,7 @@
     "testSetVariableMetaData": "mocha --timeout 9999999 -r ts-node/register ./**/setVariableMetaData.test.ts",
     "testSetCollectionLimits": "mocha --timeout 9999999 -r ts-node/register ./**/setCollectionLimits.test.ts",
     "testRemoveCollectionAdmin": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionAdmin.test.ts",
+    "testRemoveCollectionSponsor": "mocha --timeout 9999999 -r ts-node/register ./**/removeCollectionSponsor.test.ts",
     "testRemoveFromWhiteList": "mocha --timeout 9999999 -r ts-node/register ./**/removeFromWhiteList.test.ts",
     "testConnection": "mocha --timeout 9999999 -r ts-node/register ./**/connection.test.ts",
     "testCollection": "mocha --timeout 9999999 -r ts-node/register ./**/createCollection.test.ts",
modifiedtests/src/util/helpers.tsdiffbeforeafterboth
--- a/tests/src/util/helpers.ts
+++ b/tests/src/util/helpers.ts
@@ -396,8 +396,7 @@
 
     // What to expect
     expect(result.success).to.be.true;
-    expect(collection.Sponsor).to.be.equal(nullPublicKey);
-    expect(collection.SponsorConfirmed).to.be.false;
+    expect(collection.Sponsorship).to.be.deep.equal({ Disabled: null });
   });
 }