git.delta.rocks / unique-network / refs/commits / 05f22b5514d1

difftreelog

refactor review unwraps

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

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use 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    transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_runtime::sp_std::prelude::Vec;36use sp_runtime::{37    traits::{38        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,39    },40    transaction_validity::{41        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,42    },43    FixedPointOperand, FixedU128,44};45use sp_runtime::traits::StaticLookup;46use pallet_contracts::chain_extension::UncheckedFrom;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;5657pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;58pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;59pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;60pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6162// Structs63// #region6465pub type CollectionId = u32;66pub type TokenId = u32;67pub type DecimalPoints = u8;6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum CollectionMode {72    Invalid,73    NFT,74    // decimal points75    Fungible(DecimalPoints),76    ReFungible,77}7879impl Default for CollectionMode {80    fn default() -> Self {81        Self::Invalid82    }83}8485impl Into<u8> for CollectionMode {86    fn into(self) -> u8 {87        match self {88            CollectionMode::Invalid => 0,89            CollectionMode::NFT => 1,90            CollectionMode::Fungible(_) => 2,91            CollectionMode::ReFungible => 3,92        }93    }94}9596#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]97#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]98pub enum AccessMode {99    Normal,100    WhiteList,101}102impl Default for AccessMode {103    fn default() -> Self {104        Self::Normal105    }106}107108#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]109#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]110pub enum SchemaVersion {111    ImageURL,112    Unique,113}114impl Default for SchemaVersion {115    fn default() -> Self {116        Self::ImageURL117    }118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct Ownership<AccountId> {123    pub owner: AccountId,124    pub fraction: u128,125}126127#[derive(Encode, Decode, Debug, Clone, PartialEq)]128#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]129pub enum SponsorshipState<AccountId> {130    /// The fees are applied to the transaction sender131    Disabled,132    Unconfirmed(AccountId),133    /// Transactions are sponsored by specified account134    Confirmed(AccountId),135}136137impl<AccountId> SponsorshipState<AccountId> {138    fn sponsor(&self) -> Option<&AccountId> {139        match self {140            Self::Confirmed(sponsor) => Some(sponsor),141            _ => None,142        }143    }144145    fn pending_sponsor(&self) -> Option<&AccountId> {146        match self {147            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),148            _ => None,149        }150    }151152    fn confirmed(&self) -> bool {153        matches!(self, Self::Confirmed(_))154    }155}156157impl<T> Default for SponsorshipState<T> {158    fn default() -> Self {159        Self::Disabled160    }161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct CollectionType<AccountId> {166    pub owner: AccountId,167    pub mode: CollectionMode,168    pub access: AccessMode,169    pub decimal_points: DecimalPoints,170    pub name: Vec<u16>,        // 64 include null escape char171    pub description: Vec<u16>, // 256 include null escape char172    pub token_prefix: Vec<u8>, // 16 include null escape char173    pub mint_mode: bool,174    pub offchain_schema: Vec<u8>,175    pub schema_version: SchemaVersion,176    pub sponsorship: SponsorshipState<AccountId>,177    pub limits: CollectionLimits, // Collection private restrictions 178    pub variable_on_chain_schema: Vec<u8>, //179    pub const_on_chain_schema: Vec<u8>, //180}181182#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]183#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]184pub struct NftItemType<AccountId> {185    pub owner: AccountId,186    pub const_data: Vec<u8>,187    pub variable_data: Vec<u8>,188}189190#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]191#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]192pub struct FungibleItemType {193    pub value: u128,194}195196#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct ReFungibleItemType<AccountId> {199    pub owner: Vec<Ownership<AccountId>>,200    pub const_data: Vec<u8>,201    pub variable_data: Vec<u8>,202}203204// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]205// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206// pub struct VestingItem<AccountId, Moment> {207//     pub sender: AccountId,208//     pub recipient: AccountId,209//     pub collection_id: CollectionId,210//     pub item_id: TokenId,211//     pub amount: u64,212//     pub vesting_date: Moment,213// }214215#[derive(Encode, Decode, Debug, Clone, PartialEq)]216#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]217pub struct CollectionLimits {218    pub account_token_ownership_limit: u32,219    pub sponsored_data_size: u32,220    pub token_limit: u32,221222    // Timeouts for item types in passed blocks223    pub sponsor_transfer_timeout: u32,224    pub owner_can_transfer: bool,225    pub owner_can_destroy: bool,226}227228impl Default for CollectionLimits {229    fn default() -> CollectionLimits {230        CollectionLimits { 231            account_token_ownership_limit: 10_000_000, 232            token_limit: u32::max_value(),233            sponsored_data_size: u32::MAX,234            sponsor_transfer_timeout: 14400,235            owner_can_transfer: true,236            owner_can_destroy: true237        }238    }239}240241#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]242#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]243pub struct ChainLimits {244    pub collection_numbers_limit: u32,245    pub account_token_ownership_limit: u32,246    pub collections_admins_limit: u64,247    pub custom_data_limit: u32,248249    // Timeouts for item types in passed blocks250    pub nft_sponsor_transfer_timeout: u32,251    pub fungible_sponsor_transfer_timeout: u32,252    pub refungible_sponsor_transfer_timeout: u32,253254    // Schema limits255    pub offchain_schema_limit: u32,256    pub variable_on_chain_schema_limit: u32,257    pub const_on_chain_schema_limit: u32,258}259260pub trait WeightInfo {261	fn create_collection() -> Weight;262	fn destroy_collection() -> Weight;263	fn add_to_white_list() -> Weight;264	fn remove_from_white_list() -> Weight;265    fn set_public_access_mode() -> Weight;266    fn set_mint_permission() -> Weight;267    fn change_collection_owner() -> Weight;268    fn add_collection_admin() -> Weight;269    fn remove_collection_admin() -> Weight;270    fn set_collection_sponsor() -> Weight;271    fn confirm_sponsorship() -> Weight;272    fn remove_collection_sponsor() -> Weight;273    fn create_item(s: usize) -> Weight;274    fn burn_item() -> Weight;275    fn transfer() -> Weight;276    fn approve() -> Weight;277    fn transfer_from() -> Weight;278    fn set_offchain_schema() -> Weight;279    fn set_const_on_chain_schema() -> Weight;280    fn set_variable_on_chain_schema() -> Weight;281    fn set_variable_meta_data() -> Weight;282    fn enable_contract_sponsoring() -> Weight;283    fn set_schema_version() -> Weight;284    fn set_chain_limits() -> Weight;285    fn set_contract_sponsoring_rate_limit() -> Weight;286    fn toggle_contract_white_list() -> Weight;287    fn add_to_contract_white_list() -> Weight;288    fn remove_from_contract_white_list() -> Weight;289    fn set_collection_limits() -> Weight;290}291292#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]293#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]294pub struct CreateNftData {295    pub const_data: Vec<u8>,296    pub variable_data: Vec<u8>,297}298299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]300#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]301pub struct CreateFungibleData {302    pub value: u128,303}304305#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]306#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]307pub struct CreateReFungibleData {308    pub const_data: Vec<u8>,309    pub variable_data: Vec<u8>,310    pub pieces: u128,311}312313#[derive(Encode, Decode, Debug, Clone, PartialEq)]314#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]315pub enum CreateItemData {316    NFT(CreateNftData),317    Fungible(CreateFungibleData),318    ReFungible(CreateReFungibleData),319}320321impl CreateItemData {322    pub fn len(&self) -> usize {323        let len = match self {324            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),325            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),326            _ => 0327        };328        329        return len;330    }331}332333impl From<CreateNftData> for CreateItemData {334    fn from(item: CreateNftData) -> Self {335        CreateItemData::NFT(item)336    }337}338339impl From<CreateReFungibleData> for CreateItemData {340    fn from(item: CreateReFungibleData) -> Self {341        CreateItemData::ReFungible(item)342    }343}344345impl From<CreateFungibleData> for CreateItemData {346    fn from(item: CreateFungibleData) -> Self {347        CreateItemData::Fungible(item)348    }349}350351352decl_error! {353	/// Error for non-fungible-token module.354	pub enum Error for Module<T: Config> {355        /// Total collections bound exceeded.356        TotalCollectionsLimitExceeded,357		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.358        CollectionDecimalPointLimitExceeded, 359        /// Collection name can not be longer than 63 char.360        CollectionNameLimitExceeded, 361        /// Collection description can not be longer than 255 char.362        CollectionDescriptionLimitExceeded, 363        /// Token prefix can not be longer than 15 char.364        CollectionTokenPrefixLimitExceeded,365        /// This collection does not exist.366        CollectionNotFound,367        /// Item not exists.368        TokenNotFound,369        /// Admin not found370        AdminNotFound,371        /// Arithmetic calculation overflow.372        NumOverflow,       373        /// Account already has admin role.374        AlreadyAdmin,  375        /// You do not own this collection.376        NoPermission,377        /// This address is not set as sponsor, use setCollectionSponsor first.378        ConfirmUnsetSponsorFail,379        /// Collection is not in mint mode.380        PublicMintingNotAllowed,381        /// Sender parameter and item owner must be equal.382        MustBeTokenOwner,383        /// Item balance not enough.384        TokenValueTooLow,385        /// Size of item is too large.386        NftSizeLimitExceeded,387        /// No approve found388        ApproveNotFound,389        /// Requested value more than approved.390        TokenValueNotEnough,391        /// Only approved addresses can call this method.392        ApproveRequired,393        /// Address is not in white list.394        AddresNotInWhiteList,395        /// Number of collection admins bound exceeded.396        CollectionAdminsLimitExceeded,397        /// Owned tokens by a single address bound exceeded.398        AddressOwnershipLimitExceeded,399        /// Length of items properties must be greater than 0.400        EmptyArgument,401        /// const_data exceeded data limit.402        TokenConstDataLimitExceeded,403        /// variable_data exceeded data limit.404        TokenVariableDataLimitExceeded,405        /// Not NFT item data used to mint in NFT collection.406        NotNftDataUsedToMintNftCollectionToken,407        /// Not Fungible item data used to mint in Fungible collection.408        NotFungibleDataUsedToMintFungibleCollectionToken,409        /// Not Re Fungible item data used to mint in Re Fungible collection.410        NotReFungibleDataUsedToMintReFungibleCollectionToken,411        /// Unexpected collection type.412        UnexpectedCollectionType,413        /// Can't store metadata in fungible tokens.414        CantStoreMetadataInFungibleTokens,415        /// Collection token limit exceeded416        CollectionTokenLimitExceeded,417        /// Account token limit exceeded per collection418        AccountTokenLimitExceeded,419        /// Collection limit bounds per collection exceeded420        CollectionLimitBoundsExceeded,421        /// Tried to enable permissions which are only permitted to be disabled422        OwnerPermissionsCantBeReverted,423        /// Schema data size limit bound exceeded424        SchemaDataLimitExceeded,425        /// Maximum refungibility exceeded426        WrongRefungiblePieces427	}428}429430pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {431    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;432433    /// Weight information for extrinsics in this pallet.434	type WeightInfo: WeightInfo;435}436437#[cfg(feature = "runtime-benchmarks")]438mod benchmarking;439440// #endregion441442// # Used definitions443//444// ## User control levels445//446// chain-controlled - key is uncontrolled by user447//                    i.e autoincrementing index448//                    can use non-cryptographic hash449// real - key is controlled by user450//        but it is hard to generate enough colliding values, i.e owner of signed txs451//        can use non-cryptographic hash452// controlled - key is completly controlled by users453//              i.e maps with mutable keys454//              should use cryptographic hash455//456// ## User control level downgrade reasons457//458// ?1 - chain-controlled -> controlled459//      collections/tokens can be destroyed, resulting in massive holes460// ?2 - chain-controlled -> controlled461//      same as ?1, but can be only added, resulting in easier exploitation462// ?3 - real -> controlled463//      no confirmation required, so addresses can be easily generated464decl_storage! {465    trait Store for Module<T: Config> as Nft {466467        //#region Private members468        /// Id of next collection469        CreatedCollectionCount: u32;470        /// Used for migrations471        ChainVersion: u64;472        /// Id of last collection token473        /// Collection id (controlled?1)474        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;475        //#endregion476477        //#region Chain limits struct478        pub ChainLimit get(fn chain_limit) config(): ChainLimits;479        //#endregion480481        //#region Bound counters482        /// Amount of collections destroyed, used for total amount tracking with483        /// CreatedCollectionCount484        DestroyedCollectionCount: u32;485        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)486        /// Account id (real)487        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;488        //#endregion489490        //#region Basic collections491        /// Collection info492        /// Collection id (controlled?1)493        pub Collection get(fn collection) config(): map hasher(blake2_128_concat) CollectionId => CollectionType<T::AccountId>;494        /// List of collection admins495        /// Collection id (controlled?2)496        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;497        /// Whitelisted collection users498        /// Collection id (controlled?2), user id (controlled?3)499        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;500        //#endregion501502        /// How many of collection items user have503        /// Collection id (controlled?2), account id (real)504        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;505506        /// Amount of items which spender can transfer out of owners account (via transferFrom)507        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))508        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;509510        //#region Item collections511        /// Collection id (controlled?2), token id (controlled?1)512        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;513        /// Collection id (controlled?2), owner (controlled?2)514        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;515        /// Collection id (controlled?2), token id (controlled?1)516        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;517        //#endregion518519        //#region Index list520        /// Collection id (controlled?2), tokens owner (controlled?2)521        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;522        //#endregion523524        //#region Tokens transfer rate limit baskets525        /// (Collection id (controlled?2), who created (real))526        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;527        /// Collection id (controlled?2), token id (controlled?2)528        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;529        /// Collection id (controlled?2), owning user (real)530        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;531        /// Collection id (controlled?2), token id (controlled?2)532        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;533        //#endregion534535        //#region Contract Sponsorship and Ownership536        /// Contract address (real)537        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;538        /// Contract address (real)539        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;540        /// (Contract address(real), caller (real))541        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;542        /// Contract address (real)543        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;544        /// Contract address (real)545        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 546        /// Contract address (real) => Whitelisted user (controlled?3)547        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 548        //#endregion549    }550    add_extra_genesis {551        build(|config: &GenesisConfig<T>| {552            // Modification of storage553            for (_num, _c) in &config.collection {554                <Module<T>>::init_collection(_c);555            }556557            for (_num, _c, _i) in &config.nft_item_id {558                <Module<T>>::init_nft_token(*_c, _i);559            }560561            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {562                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);563            }564565            for (_num, _c, _i) in &config.refungible_item_id {566                <Module<T>>::init_refungible_token(*_c, _i);567            }568        })569    }570}571572decl_event!(573    pub enum Event<T>574    where575        AccountId = <T as system::Config>::AccountId,576    {577        /// New collection was created578        /// 579        /// # Arguments580        /// 581        /// * collection_id: Globally unique identifier of newly created collection.582        /// 583        /// * mode: [CollectionMode] converted into u8.584        /// 585        /// * account_id: Collection owner.586        Created(CollectionId, u8, AccountId),587588        /// New item was created.589        /// 590        /// # Arguments591        /// 592        /// * collection_id: Id of the collection where item was created.593        /// 594        /// * item_id: Id of an item. Unique within the collection.595        ///596        /// * recipient: Owner of newly created item 597        ItemCreated(CollectionId, TokenId, AccountId),598599        /// Collection item was burned.600        /// 601        /// # Arguments602        /// 603        /// collection_id.604        /// 605        /// item_id: Identifier of burned NFT.606        ItemDestroyed(CollectionId, TokenId),607608        /// Item was transferred609        ///610        /// * collection_id: Id of collection to which item is belong611        ///612        /// * item_id: Id of an item613        ///614        /// * sender: Original owner of item615        ///616        /// * recipient: New owner of item617        ///618        /// * amount: Always 1 for NFT619        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),620    }621);622623decl_module! {624    pub struct Module<T: Config> for enum Call 625    where 626        origin: T::Origin627    {628        fn deposit_event() = default;629        type Error = Error<T>;630631        fn on_initialize(now: T::BlockNumber) -> Weight {632            0633        }634635        /// 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.636        /// 637        /// # Permissions638        /// 639        /// * Anyone.640        /// 641        /// # Arguments642        /// 643        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.644        /// 645        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.646        /// 647        /// * token_prefix: UTF-8 string with token prefix.648        /// 649        /// * mode: [CollectionMode] collection type and type dependent data.650        // returns collection ID651        #[weight = <T as Config>::WeightInfo::create_collection()]652        #[transactional]653        pub fn create_collection(origin,654                                 collection_name: Vec<u16>,655                                 collection_description: Vec<u16>,656                                 token_prefix: Vec<u8>,657                                 mode: CollectionMode) -> DispatchResult {658659            // Anyone can create a collection660            let who = ensure_signed(origin)?;661662            let decimal_points = match mode {663                CollectionMode::Fungible(points) => points,664                _ => 0665            };666667            let chain_limit = ChainLimit::get();668669            let created_count = CreatedCollectionCount::get();670            let destroyed_count = DestroyedCollectionCount::get();671672            // bound Total number of collections673            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);674675            // check params676            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);677            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);678            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);679            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);680681            // Generate next collection ID682            let next_id = created_count683                .checked_add(1)684                .ok_or(Error::<T>::NumOverflow)?;685686            CreatedCollectionCount::put(next_id);687688            let limits = CollectionLimits {689                sponsored_data_size: chain_limit.custom_data_limit,690                ..Default::default()691            };692693            // Create new collection694            let new_collection = CollectionType {695                owner: who.clone(),696                name: collection_name,697                mode: mode.clone(),698                mint_mode: false,699                access: AccessMode::Normal,700                description: collection_description,701                decimal_points: decimal_points,702                token_prefix: token_prefix,703                offchain_schema: Vec::new(),704                schema_version: SchemaVersion::ImageURL,705                sponsorship: SponsorshipState::Disabled,706                variable_on_chain_schema: Vec::new(),707                const_on_chain_schema: Vec::new(),708                limits,709            };710711            // Add new collection to map712            <Collection<T>>::insert(next_id, new_collection);713714            // call event715            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));716717            Ok(())718        }719720        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.721        /// 722        /// # Permissions723        /// 724        /// * Collection Owner.725        /// 726        /// # Arguments727        /// 728        /// * collection_id: collection to destroy.729        #[weight = <T as Config>::WeightInfo::destroy_collection()]730        #[transactional]731        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {732733            let sender = ensure_signed(origin)?;734            Self::check_owner_permissions(collection_id, sender)?;735736            let target_collection = <Collection<T>>::get(collection_id);737            if !target_collection.limits.owner_can_destroy {738                fail!(Error::<T>::NoPermission);739            }740741            <AddressTokens<T>>::remove_prefix(collection_id);742            <Allowances<T>>::remove_prefix(collection_id);743            <Balance<T>>::remove_prefix(collection_id);744            <ItemListIndex>::remove(collection_id);745            <AdminList<T>>::remove(collection_id);746            <Collection<T>>::remove(collection_id);747            <WhiteList<T>>::remove_prefix(collection_id);748749            <NftItemList<T>>::remove_prefix(collection_id);750            <FungibleItemList<T>>::remove_prefix(collection_id);751            <ReFungibleItemList<T>>::remove_prefix(collection_id);752753            <NftTransferBasket<T>>::remove_prefix(collection_id);754            <FungibleTransferBasket<T>>::remove_prefix(collection_id);755            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);756757            DestroyedCollectionCount::put(DestroyedCollectionCount::get()758                .checked_add(1)759                .ok_or(Error::<T>::NumOverflow)?);760761            Ok(())762        }763764        /// Add an address to white list.765        /// 766        /// # Permissions767        /// 768        /// * Collection Owner769        /// * Collection Admin770        /// 771        /// # Arguments772        /// 773        /// * collection_id.774        /// 775        /// * address.776        #[weight = <T as Config>::WeightInfo::add_to_white_list()]777        #[transactional]778        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{779780            let sender = ensure_signed(origin)?;781            Self::check_owner_or_admin_permissions(collection_id, sender)?;782783            <WhiteList<T>>::insert(collection_id, address, true);784            785            Ok(())786        }787788        /// Remove an address from white list.789        /// 790        /// # Permissions791        /// 792        /// * Collection Owner793        /// * Collection Admin794        /// 795        /// # Arguments796        /// 797        /// * collection_id.798        /// 799        /// * address.800        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]801        #[transactional]802        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{803804            let sender = ensure_signed(origin)?;805            Self::check_owner_or_admin_permissions(collection_id, sender)?;806807            <WhiteList<T>>::remove(collection_id, address);808809            Ok(())810        }811812        /// Toggle between normal and white list access for the methods with access for `Anyone`.813        /// 814        /// # Permissions815        /// 816        /// * Collection Owner.817        /// 818        /// # Arguments819        /// 820        /// * collection_id.821        /// 822        /// * mode: [AccessMode]823        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]824        #[transactional]825        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult826        {827            let sender = ensure_signed(origin)?;828829            Self::check_owner_permissions(collection_id, sender)?;830            let mut target_collection = <Collection<T>>::get(collection_id);831            target_collection.access = mode;832            <Collection<T>>::insert(collection_id, target_collection);833834            Ok(())835        }836837        /// Allows Anyone to create tokens if:838        /// * White List is enabled, and839        /// * Address is added to white list, and840        /// * This method was called with True parameter841        /// 842        /// # Permissions843        /// * Collection Owner844        ///845        /// # Arguments846        /// 847        /// * collection_id.848        /// 849        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.850        #[weight = <T as Config>::WeightInfo::set_mint_permission()]851        #[transactional]852        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult853        {854            let sender = ensure_signed(origin)?;855856            Self::check_owner_permissions(collection_id, sender)?;857            let mut target_collection = <Collection<T>>::get(collection_id);858            target_collection.mint_mode = mint_permission;859            <Collection<T>>::insert(collection_id, target_collection);860861            Ok(())862        }863864        /// Change the owner of the collection.865        /// 866        /// # Permissions867        /// 868        /// * Collection Owner.869        /// 870        /// # Arguments871        /// 872        /// * collection_id.873        /// 874        /// * new_owner.875        #[weight = <T as Config>::WeightInfo::change_collection_owner()]876        #[transactional]877        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {878879            let sender = ensure_signed(origin)?;880            Self::check_owner_permissions(collection_id, sender)?;881            let mut target_collection = <Collection<T>>::get(collection_id);882            target_collection.owner = new_owner;883            <Collection<T>>::insert(collection_id, target_collection);884885            Ok(())886        }887888        /// Adds an admin of the Collection.889        /// 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. 890        /// 891        /// # Permissions892        /// 893        /// * Collection Owner.894        /// * Collection Admin.895        /// 896        /// # Arguments897        /// 898        /// * collection_id: ID of the Collection to add admin for.899        /// 900        /// * new_admin_id: Address of new admin to add.901        #[weight = <T as Config>::WeightInfo::add_collection_admin()]902        #[transactional]903        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {904905            let sender = ensure_signed(origin)?;906            Self::check_owner_or_admin_permissions(collection_id, sender)?;907            let mut admin_arr: Vec<T::AccountId> = Vec::new();908909            if <AdminList<T>>::contains_key(collection_id)910            {911                admin_arr = <AdminList<T>>::get(collection_id);912                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);913            }914915            // Number of collection admins916            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);917918            admin_arr.push(new_admin_id);919            <AdminList<T>>::insert(collection_id, admin_arr);920921            Ok(())922        }923924        /// 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.925        ///926        /// # Permissions927        /// 928        /// * Collection Owner.929        /// * Collection Admin.930        /// 931        /// # Arguments932        /// 933        /// * collection_id: ID of the Collection to remove admin for.934        /// 935        /// * account_id: Address of admin to remove.936        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]937        #[transactional]938        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {939940            let sender = ensure_signed(origin)?;941            Self::check_owner_or_admin_permissions(collection_id, sender)?;942            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);943944            let mut admin_arr = <AdminList<T>>::get(collection_id);945            admin_arr.retain(|i| *i != account_id);946            <AdminList<T>>::insert(collection_id, admin_arr);947948            Ok(())949        }950951        /// # Permissions952        /// 953        /// * Collection Owner954        /// 955        /// # Arguments956        /// 957        /// * collection_id.958        /// 959        /// * new_sponsor.960        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]961        #[transactional]962        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {963964            let sender = ensure_signed(origin)?;965            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);966967            let mut target_collection = <Collection<T>>::get(collection_id);968            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);969970            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);971            <Collection<T>>::insert(collection_id, target_collection);972973            Ok(())974        }975976        /// # Permissions977        /// 978        /// * Sponsor.979        /// 980        /// # Arguments981        /// 982        /// * collection_id.983        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]984        #[transactional]985        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {986987            let sender = ensure_signed(origin)?;988            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);989990            let mut target_collection = <Collection<T>>::get(collection_id);991            ensure!(992                target_collection.sponsorship.pending_sponsor() == Some(&sender),993                Error::<T>::ConfirmUnsetSponsorFail994            );995996            target_collection.sponsorship = SponsorshipState::Confirmed(sender);997            <Collection<T>>::insert(collection_id, target_collection);998999            Ok(())1000        }10011002        /// Switch back to pay-per-own-transaction model.1003        ///1004        /// # Permissions1005        ///1006        /// * Collection owner.1007        /// 1008        /// # Arguments1009        /// 1010        /// * collection_id.1011        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1012        #[transactional]1013        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10141015            let sender = ensure_signed(origin)?;1016            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);10171018            let mut target_collection = <Collection<T>>::get(collection_id);1019            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);10201021            target_collection.sponsorship = SponsorshipState::Disabled;1022            <Collection<T>>::insert(collection_id, target_collection);10231024            Ok(())1025        }10261027        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner.1032        /// * Collection Admin.1033        /// * Anyone if1034        ///     * White List is enabled, and1035        ///     * Address is added to white list, and1036        ///     * MintPermission is enabled (see SetMintPermission method)1037        /// 1038        /// # Arguments1039        /// 1040        /// * collection_id: ID of the collection.1041        /// 1042        /// * owner: Address, initial owner of the NFT.1043        ///1044        /// * data: Token data to store on chain.1045        // #[weight =1046        // (130_000_000 as Weight)1047        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1048        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1049        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]10501051        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1052        #[transactional]1053        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {10541055            let sender = ensure_signed(origin)?;10561057            Self::collection_exists(collection_id)?;10581059            let target_collection = <Collection<T>>::get(collection_id);10601061            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;1062            Self::validate_create_item_args(&target_collection, &data)?;1063            Self::create_item_no_validation(collection_id, owner, data)?;10641065            Ok(())1066        }10671068        /// This method creates multiple items in a collection created with CreateCollection method.1069        /// 1070        /// # Permissions1071        /// 1072        /// * Collection Owner.1073        /// * Collection Admin.1074        /// * Anyone if1075        ///     * White List is enabled, and1076        ///     * Address is added to white list, and1077        ///     * MintPermission is enabled (see SetMintPermission method)1078        /// 1079        /// # Arguments1080        /// 1081        /// * collection_id: ID of the collection.1082        /// 1083        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1084        /// 1085        /// * owner: Address, initial owner of the NFT.1086        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1087                               .map(|data| { data.len() })1088                               .sum())]1089        #[transactional]1090        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10911092            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1093            let sender = ensure_signed(origin)?;10941095            Self::collection_exists(collection_id)?;1096            let target_collection = <Collection<T>>::get(collection_id);10971098            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10991100            for data in &items_data {1101                Self::validate_create_item_args(&target_collection, data)?;1102            }1103            for data in &items_data {1104                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1105            }11061107            Ok(())1108        }11091110        /// Destroys a concrete instance of NFT.1111        /// 1112        /// # Permissions1113        /// 1114        /// * Collection Owner.1115        /// * Collection Admin.1116        /// * Current NFT Owner.1117        /// 1118        /// # Arguments1119        /// 1120        /// * collection_id: ID of the collection.1121        /// 1122        /// * item_id: ID of NFT to burn.1123        #[weight = <T as Config>::WeightInfo::burn_item()]1124        #[transactional]1125        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11261127            let sender = ensure_signed(origin)?;1128            Self::collection_exists(collection_id)?;11291130            // Transfer permissions check1131            let target_collection = <Collection<T>>::get(collection_id);1132            ensure!(1133                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1134                (1135                    target_collection.limits.owner_can_transfer &&1136                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1137                ),1138                Error::<T>::NoPermission1139            );11401141            if target_collection.access == AccessMode::WhiteList {1142                Self::check_white_list(collection_id, &sender)?;1143            }11441145            match target_collection.mode1146            {1147                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1148                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1149                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1150                _ => ()1151            };11521153            // call event1154            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));11551156            Ok(())1157        }11581159        /// Change ownership of the token.1160        /// 1161        /// # Permissions1162        /// 1163        /// * Collection Owner1164        /// * Collection Admin1165        /// * Current NFT owner1166        ///1167        /// # Arguments1168        /// 1169        /// * recipient: Address of token recipient.1170        /// 1171        /// * collection_id.1172        /// 1173        /// * item_id: ID of the item1174        ///     * Non-Fungible Mode: Required.1175        ///     * Fungible Mode: Ignored.1176        ///     * Re-Fungible Mode: Required.1177        /// 1178        /// * value: Amount to transfer.1179        ///     * Non-Fungible Mode: Ignored1180        ///     * Fungible Mode: Must specify transferred amount1181        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1182        #[weight = <T as Config>::WeightInfo::transfer()]1183        #[transactional]1184        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1185            let sender = ensure_signed(origin)?;1186            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1187        }11881189        /// Set, change, or remove approved address to transfer the ownership of the NFT.1190        /// 1191        /// # Permissions1192        /// 1193        /// * Collection Owner1194        /// * Collection Admin1195        /// * Current NFT owner1196        /// 1197        /// # Arguments1198        /// 1199        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1200        /// 1201        /// * collection_id.1202        /// 1203        /// * item_id: ID of the item.1204        #[weight = <T as Config>::WeightInfo::approve()]1205        #[transactional]1206        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12071208            let sender = ensure_signed(origin)?;12091210            Self::collection_exists(collection_id)?;1211            Self::token_exists(collection_id, item_id, &sender)?;12121213            // Transfer permissions check1214            let target_collection = <Collection<T>>::get(collection_id);1215            let allowance_limit = if target_collection.limits.owner_can_transfer &&1216                Self::is_owner_or_admin_permissions(1217                    collection_id,1218                    sender.clone(),1219                ) {1220                None1221            } else if let Some(amount) = Self::owned_amount(1222                sender.clone(),1223                collection_id,1224                item_id,1225            ) {1226                Some(amount)1227            } else {1228                fail!(Error::<T>::NoPermission);1229            };12301231            if target_collection.access == AccessMode::WhiteList {1232                Self::check_white_list(collection_id, &sender)?;1233                Self::check_white_list(collection_id, &spender)?;1234            }12351236            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1237            let mut allowance: u128 = amount;1238            if allowance_exists {1239                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1240            }1241            if let Some(limit) = allowance_limit {1242                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1243            }1244            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12451246            Ok(())1247        }1248        1249        /// 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.1250        /// 1251        /// # Permissions1252        /// * Collection Owner1253        /// * Collection Admin1254        /// * Current NFT owner1255        /// * Address approved by current NFT owner1256        /// 1257        /// # Arguments1258        /// 1259        /// * from: Address that owns token.1260        /// 1261        /// * recipient: Address of token recipient.1262        /// 1263        /// * collection_id.1264        /// 1265        /// * item_id: ID of the item.1266        /// 1267        /// * value: Amount to transfer.1268        #[weight = <T as Config>::WeightInfo::transfer_from()]1269        #[transactional]1270        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12711272            let sender = ensure_signed(origin)?;1273            let mut appoved_transfer = false;12741275            // Check approval1276            let mut approval: u128 = 0;1277            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1278                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1279                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1280                appoved_transfer = true;1281            }12821283            let target_collection = <Collection<T>>::get(collection_id);12841285            // Limits check1286            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12871288            // Transfer permissions check         1289            ensure!(1290                appoved_transfer || 1291                (1292                    target_collection.limits.owner_can_transfer &&1293                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1294                ),1295                Error::<T>::NoPermission1296            );12971298            if target_collection.access == AccessMode::WhiteList {1299                Self::check_white_list(collection_id, &sender)?;1300                Self::check_white_list(collection_id, &recipient)?;1301            }13021303            // Reduce approval by transferred amount or remove if remaining approval drops to 01304            if approval.checked_sub(value).unwrap_or(0) > 0 {1305                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1306            }1307            else {1308                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1309            }13101311            match target_collection.mode1312            {1313                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1314                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1315                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1316                _ => ()1317            };13181319            Ok(())1320        }13211322        // #[weight = 0]1323        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13241325        //     // let no_perm_mes = "You do not have permissions to modify this collection";1326        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1327        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1328        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13291330        //     // // on_nft_received  call13311332        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13331334        //     Ok(())1335        // }13361337        /// Set off-chain data schema.1338        /// 1339        /// # Permissions1340        /// 1341        /// * Collection Owner1342        /// * Collection Admin1343        /// 1344        /// # Arguments1345        /// 1346        /// * collection_id.1347        /// 1348        /// * schema: String representing the offchain data schema.1349        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1350        #[transactional]1351        pub fn set_variable_meta_data (1352            origin,1353            collection_id: CollectionId,1354            item_id: TokenId,1355            data: Vec<u8>1356        ) -> DispatchResult {1357            let sender = ensure_signed(origin)?;1358            1359            Self::collection_exists(collection_id)?;1360            Self::token_exists(collection_id, item_id, &sender)?;13611362            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13631364            // Modify permissions check1365            let target_collection = <Collection<T>>::get(collection_id);1366            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1367                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1368                Error::<T>::NoPermission);13691370            match target_collection.mode1371            {1372                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1373                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1374                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1375                _ => fail!(Error::<T>::UnexpectedCollectionType)1376            };13771378            Ok(())1379        }1380 1381        /// Set schema standard1382        /// ImageURL1383        /// Unique1384        /// 1385        /// # Permissions1386        /// 1387        /// * Collection Owner1388        /// * Collection Admin1389        /// 1390        /// # Arguments1391        /// 1392        /// * collection_id.1393        /// 1394        /// * schema: SchemaVersion: enum1395        #[weight = <T as Config>::WeightInfo::set_schema_version()]1396        #[transactional]1397        pub fn set_schema_version(1398            origin,1399            collection_id: CollectionId,1400            version: SchemaVersion1401        ) -> DispatchResult {1402            let sender = ensure_signed(origin)?;1403            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1404            let mut target_collection = <Collection<T>>::get(collection_id);1405            target_collection.schema_version = version;1406            <Collection<T>>::insert(collection_id, target_collection);14071408            Ok(())1409        }14101411        /// Set off-chain data schema.1412        /// 1413        /// # Permissions1414        /// 1415        /// * Collection Owner1416        /// * Collection Admin1417        /// 1418        /// # Arguments1419        /// 1420        /// * collection_id.1421        /// 1422        /// * schema: String representing the offchain data schema.1423        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1424        #[transactional]1425        pub fn set_offchain_schema(1426            origin,1427            collection_id: CollectionId,1428            schema: Vec<u8>1429        ) -> DispatchResult {1430            let sender = ensure_signed(origin)?;1431            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14321433            // check schema limit1434            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14351436            let mut target_collection = <Collection<T>>::get(collection_id);1437            target_collection.offchain_schema = schema;1438            <Collection<T>>::insert(collection_id, target_collection);14391440            Ok(())1441        }14421443        /// Set const on-chain data schema.1444        /// 1445        /// # Permissions1446        /// 1447        /// * Collection Owner1448        /// * Collection Admin1449        /// 1450        /// # Arguments1451        /// 1452        /// * collection_id.1453        /// 1454        /// * schema: String representing the const on-chain data schema.1455        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1456        #[transactional]1457        pub fn set_const_on_chain_schema (1458            origin,1459            collection_id: CollectionId,1460            schema: Vec<u8>1461        ) -> DispatchResult {1462            let sender = ensure_signed(origin)?;1463            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14641465            // check schema limit1466            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14671468            let mut target_collection = <Collection<T>>::get(collection_id);1469            target_collection.const_on_chain_schema = schema;1470            <Collection<T>>::insert(collection_id, target_collection);14711472            Ok(())1473        }14741475        /// Set variable on-chain data schema.1476        /// 1477        /// # Permissions1478        /// 1479        /// * Collection Owner1480        /// * Collection Admin1481        /// 1482        /// # Arguments1483        /// 1484        /// * collection_id.1485        /// 1486        /// * schema: String representing the variable on-chain data schema.1487        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1488        #[transactional]1489        pub fn set_variable_on_chain_schema (1490            origin,1491            collection_id: CollectionId,1492            schema: Vec<u8>1493        ) -> DispatchResult {1494            let sender = ensure_signed(origin)?;1495            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14961497            // check schema limit1498            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14991500            let mut target_collection = <Collection<T>>::get(collection_id);1501            target_collection.variable_on_chain_schema = schema;1502            <Collection<T>>::insert(collection_id, target_collection);15031504            Ok(())1505        }15061507        // Sudo permissions function1508        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1509        #[transactional]1510        pub fn set_chain_limits(1511            origin,1512            limits: ChainLimits1513        ) -> DispatchResult {15141515            #[cfg(not(feature = "runtime-benchmarks"))]1516            ensure_root(origin)?;15171518            <ChainLimit>::put(limits);1519            Ok(())1520        }15211522        /// Enable smart contract self-sponsoring.1523        /// 1524        /// # Permissions1525        /// 1526        /// * Contract Owner1527        /// 1528        /// # Arguments1529        /// 1530        /// * contract address1531        /// * enable flag1532        /// 1533        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1534        #[transactional]1535        pub fn enable_contract_sponsoring(1536            origin,1537            contract_address: T::AccountId,1538            enable: bool1539        ) -> DispatchResult {15401541            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)?;15471548            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1549            Ok(())1550        }15511552        /// Set the rate limit for contract sponsoring to specified number of blocks.1553        /// 1554        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1555        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1556        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1557        /// from contract endowment if there are at least B blocks between such transactions. 1558        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1559        /// 1560        /// # Permissions1561        /// 1562        /// * Contract Owner1563        /// 1564        /// # Arguments1565        /// 1566        /// -`contract_address`: Address of the contract to sponsor1567        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1568        /// 1569        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1570        #[transactional]1571        pub fn set_contract_sponsoring_rate_limit(1572            origin,1573            contract_address: T::AccountId,1574            rate_limit: T::BlockNumber1575        ) -> 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            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1583            Ok(())1584        }15851586        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1587        /// 1588        /// # Permissions1589        /// 1590        /// * Address that deployed smart contract.1591        /// 1592        /// # Arguments1593        /// 1594        /// -`contract_address`: Address of the contract.1595        /// 1596        /// - `enable`: .  1597        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1598        #[transactional]1599        pub fn toggle_contract_white_list(1600            origin,1601            contract_address: T::AccountId,1602            enable: bool1603        ) -> DispatchResult {1604            let sender = ensure_signed(origin)?;16051606            #[cfg(feature = "runtime-benchmarks")]1607            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16081609            Self::ensure_contract_owned(sender, &contract_address)?;1610            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1611            Ok(())1612        }1613        1614        /// Add an address to smart contract white list.1615        /// 1616        /// # Permissions1617        /// 1618        /// * Address that deployed smart contract.1619        /// 1620        /// # Arguments1621        /// 1622        /// -`contract_address`: Address of the contract.1623        ///1624        /// -`account_address`: Address to add.1625        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1626        #[transactional]1627        pub fn add_to_contract_white_list(1628            origin,1629            contract_address: T::AccountId,1630            account_address: T::AccountId1631        ) -> DispatchResult {1632            let sender = ensure_signed(origin)?;16331634            #[cfg(feature = "runtime-benchmarks")]1635            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1636            1637            Self::ensure_contract_owned(sender, &contract_address)?;      1638            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1639            Ok(())1640        }16411642        /// Remove an address from smart contract white list.1643        /// 1644        /// # Permissions1645        /// 1646        /// * Address that deployed smart contract.1647        /// 1648        /// # Arguments1649        /// 1650        /// -`contract_address`: Address of the contract.1651        ///1652        /// -`account_address`: Address to remove.1653        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1654        #[transactional]1655        pub fn remove_from_contract_white_list(1656            origin,1657            contract_address: T::AccountId,1658            account_address: T::AccountId1659        ) -> DispatchResult {1660            let sender = ensure_signed(origin)?;16611662            #[cfg(feature = "runtime-benchmarks")]1663            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16641665            Self::ensure_contract_owned(sender, &contract_address)?;1666            <ContractWhiteList<T>>::remove(contract_address, account_address);1667            Ok(())1668        }16691670        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1671        #[transactional]1672        pub fn set_collection_limits(1673            origin,1674            collection_id: u32,1675            new_limits: CollectionLimits,1676        ) -> DispatchResult {1677            let sender = ensure_signed(origin)?;1678            Self::check_owner_permissions(collection_id, sender.clone())?;1679            let mut target_collection = <Collection<T>>::get(collection_id);1680            let old_limits = target_collection.limits;1681            let chain_limits = ChainLimit::get();16821683            // collection bounds1684            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1685                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1686                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1687                Error::<T>::CollectionLimitBoundsExceeded);16881689            // token_limit   check  prev1690            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1691            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16921693            ensure!(1694                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1695                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1696                Error::<T>::OwnerPermissionsCantBeReverted,1697            );16981699            target_collection.limits = new_limits;1700            <Collection<T>>::insert(collection_id, target_collection);17011702            Ok(())1703        } 1704    }1705}17061707impl<T: Config> Module<T> {17081709    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {17101711        let target_collection = <Collection<T>>::get(collection_id);17121713        // Limits check1714        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;17151716        // Transfer permissions check1717        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1718            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1719            Error::<T>::NoPermission);17201721        if target_collection.access == AccessMode::WhiteList {1722            Self::check_white_list(collection_id, &sender)?;1723            Self::check_white_list(collection_id, &recipient)?;1724        }17251726        match target_collection.mode1727        {1728            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1729            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1730            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1731            _ => ()1732        };17331734        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));17351736        Ok(())1737    }173817391740    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {17411742        // check token limit and account token limit1743        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1744        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1745        1746        Ok(())1747    }17481749    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {17501751        // check token limit and account token limit1752        let total_items: u32 = ItemListIndex::get(collection_id);1753        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1754        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1755        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);17561757        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1758            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1759            Self::check_white_list(collection_id, owner)?;1760            Self::check_white_list(collection_id, sender)?;1761        }17621763        Ok(())1764    }17651766    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1767        match target_collection.mode1768        {1769            CollectionMode::NFT => {1770                if let CreateItemData::NFT(data) = data {1771                    // check sizes1772                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1773                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1774                } else {1775                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1776                }1777            },1778            CollectionMode::Fungible(_) => {1779                if let CreateItemData::Fungible(_) = data {1780                } else {1781                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1782                }1783            },1784            CollectionMode::ReFungible => {1785                if let CreateItemData::ReFungible(data) = data {17861787                    // check sizes1788                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1789                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17901791                    // Check refungibility limits1792                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1793                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1794                } else {1795                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1796                }1797            },1798            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1799        };18001801        Ok(())1802    }18031804    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1805        match data1806        {1807            CreateItemData::NFT(data) => {1808                let item = NftItemType {1809                    owner: owner.clone(),1810                    const_data: data.const_data,1811                    variable_data: data.variable_data1812                };18131814                Self::add_nft_item(collection_id, item)?;1815            },1816            CreateItemData::Fungible(data) => {1817                Self::add_fungible_item(collection_id, &owner, data.value)?;1818            },1819            CreateItemData::ReFungible(data) => {1820                let mut owner_list = Vec::new();1821                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});18221823                let item = ReFungibleItemType {1824                    owner: owner_list,1825                    const_data: data.const_data,1826                    variable_data: data.variable_data1827                };18281829                Self::add_refungible_item(collection_id, item)?;1830            }1831        };18321833        Ok(())1834    }18351836    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {18371838        // Does new owner already have an account?1839        let mut balance: u128 = 0;1840        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1841            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1842        } 18431844        // Mint 1845        let item = FungibleItemType {1846            value: balance + value1847        };1848        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);18491850        // Update balance1851        let new_balance = <Balance<T>>::get(collection_id, owner)1852            .checked_add(value)1853            .ok_or(Error::<T>::NumOverflow)?;1854        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18551856        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1857        Ok(())1858    }18591860    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1861        let current_index = <ItemListIndex>::get(collection_id)1862            .checked_add(1)1863            .ok_or(Error::<T>::NumOverflow)?;1864        let itemcopy = item.clone();18651866        let value = item.owner.first().unwrap().fraction;1867        let owner = item.owner.first().unwrap().owner.clone();18681869        Self::add_token_index(collection_id, current_index, &owner)?;18701871        <ItemListIndex>::insert(collection_id, current_index);1872        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18731874        // Update balance1875        let new_balance = <Balance<T>>::get(collection_id, &owner)1876            .checked_add(value)1877            .ok_or(Error::<T>::NumOverflow)?;1878        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);18791880        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1881        Ok(())1882    }18831884    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1885        let current_index = <ItemListIndex>::get(collection_id)1886            .checked_add(1)1887            .ok_or(Error::<T>::NumOverflow)?;18881889        let item_owner = item.owner.clone();1890        Self::add_token_index(collection_id, current_index, &item.owner)?;18911892        <ItemListIndex>::insert(collection_id, current_index);1893        <NftItemList<T>>::insert(collection_id, current_index, item);18941895        // Update balance1896        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1897            .checked_add(1)1898            .ok_or(Error::<T>::NumOverflow)?;1899        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);19001901        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1902        Ok(())1903    }19041905    fn burn_refungible_item(1906        collection_id: CollectionId,1907        item_id: TokenId,1908        owner: &T::AccountId,1909    ) -> DispatchResult {1910        ensure!(1911            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1912            Error::<T>::TokenNotFound1913        );1914        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1915        let rft_balance = token1916            .owner1917            .iter()1918            .filter(|&i| i.owner == *owner)1919            .next()1920            .unwrap();1921        Self::remove_token_index(collection_id, item_id, owner)?;19221923        // update balance1924        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1925            .checked_sub(rft_balance.fraction)1926            .ok_or(Error::<T>::NumOverflow)?;1927        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);19281929        // Re-create owners list with sender removed1930        let index = token1931            .owner1932            .iter()1933            .position(|i| i.owner == *owner)1934            .unwrap();1935        token.owner.remove(index);1936        let owner_count = token.owner.len();19371938        // Burn the token completely if this was the last (only) owner1939        if owner_count == 0 {1940            <ReFungibleItemList<T>>::remove(collection_id, item_id);1941        }1942        else {1943            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1944        }19451946        Ok(())1947    }19481949    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1950        ensure!(1951            <NftItemList<T>>::contains_key(collection_id, item_id),1952            Error::<T>::TokenNotFound1953        );1954        let item = <NftItemList<T>>::get(collection_id, item_id);1955        Self::remove_token_index(collection_id, item_id, &item.owner)?;19561957        // update balance1958        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1959            .checked_sub(1)1960            .ok_or(Error::<T>::NumOverflow)?;1961        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1962        <NftItemList<T>>::remove(collection_id, item_id);19631964        Ok(())1965    }19661967    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1968        ensure!(1969            <FungibleItemList<T>>::contains_key(collection_id, owner),1970            Error::<T>::TokenNotFound1971        );1972        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1973        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19741975        // update balance1976        let new_balance = <Balance<T>>::get(collection_id, owner)1977            .checked_sub(value)1978            .ok_or(Error::<T>::NumOverflow)?;1979        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19801981        if balance.value - value > 0 {1982            balance.value -= value;1983            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1984        }1985        else {1986            <FungibleItemList<T>>::remove(collection_id, owner);1987        }19881989        Ok(())1990    }19911992    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1993        ensure!(1994            <Collection<T>>::contains_key(collection_id),1995            Error::<T>::CollectionNotFound1996        );1997        Ok(())1998    }19992000    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {2001        Self::collection_exists(collection_id)?;20022003        let target_collection = <Collection<T>>::get(collection_id);2004        ensure!(2005            subject == target_collection.owner,2006            Error::<T>::NoPermission2007        );20082009        Ok(())2010    }20112012    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {2013        let target_collection = <Collection<T>>::get(collection_id);2014        let mut result: bool = subject == target_collection.owner;2015        let exists = <AdminList<T>>::contains_key(collection_id);20162017        if !result & exists {2018            if <AdminList<T>>::get(collection_id).contains(&subject) {2019                result = true2020            }2021        }20222023        result2024    }20252026    fn check_owner_or_admin_permissions(2027        collection_id: CollectionId,2028        subject: T::AccountId,2029    ) -> DispatchResult {2030        Self::collection_exists(collection_id)?;2031        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());20322033        ensure!(2034            result,2035            Error::<T>::NoPermission2036        );2037        Ok(())2038    }20392040    fn owned_amount(2041        subject: T::AccountId,2042        collection_id: CollectionId,2043        item_id: TokenId,2044    ) -> Option<u128> {2045        let target_collection = <Collection<T>>::get(collection_id);20462047        match target_collection.mode {2048            CollectionMode::NFT => {2049                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {2050                    return Some(1)2051                }2052                None2053            },2054            CollectionMode::Fungible(_) => {2055                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {2056                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)2057                        .value);2058                }2059                None2060            },2061            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)2062                .owner2063                .iter()2064                .find(|i| i.owner == subject)2065                .map(|i| i.fraction),2066            CollectionMode::Invalid => None,2067        }2068    }20692070    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {2071        let target_collection = <Collection<T>>::get(collection_id);20722073        match target_collection.mode {2074            CollectionMode::NFT => {2075                <NftItemList<T>>::get(collection_id, item_id).owner == subject2076            }2077            CollectionMode::Fungible(_) => {2078                <FungibleItemList<T>>::contains_key(collection_id, &subject)2079            }2080            CollectionMode::ReFungible => {2081                <ReFungibleItemList<T>>::get(collection_id, item_id)2082                    .owner2083                    .iter()2084                    .any(|i| i.owner == subject)2085            }2086            CollectionMode::Invalid => false,2087        }2088    }20892090    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {2091        let mes = Error::<T>::AddresNotInWhiteList;2092        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20932094        Ok(())2095    }20962097    /// Check if token exists. In case of Fungible, check if there is an entry for 2098    /// the owner in fungible balances double map2099    fn token_exists(2100        collection_id: CollectionId,2101        item_id: TokenId,2102        owner: &T::AccountId2103    ) -> DispatchResult {2104        let target_collection = <Collection<T>>::get(collection_id);2105        let exists = match target_collection.mode2106        {2107            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2108            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2109            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2110            _ => false2111        };21122113        ensure!(exists == true, Error::<T>::TokenNotFound);2114        Ok(())2115    }21162117    fn transfer_fungible(2118        collection_id: CollectionId,2119        value: u128,2120        owner: &T::AccountId,2121        recipient: &T::AccountId,2122    ) -> DispatchResult {2123        Self::token_exists(collection_id, 0, owner)?;21242125        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2126        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);21272128        // Send balance to recipient (updates balanceOf of recipient)2129        Self::add_fungible_item(collection_id, recipient, value)?;21302131        // update balanceOf of sender2132        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);21332134        // Reduce or remove sender2135        if balance.value == value {2136            <FungibleItemList<T>>::remove(collection_id, owner);2137        }2138        else {2139            balance.value -= value;2140            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2141        }21422143        Ok(())2144    }21452146    fn transfer_refungible(2147        collection_id: CollectionId,2148        item_id: TokenId,2149        value: u128,2150        owner: T::AccountId,2151        new_owner: T::AccountId,2152    ) -> DispatchResult {2153        Self::token_exists(collection_id, item_id, &owner)?;21542155        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2156        let item = full_item2157            .owner2158            .iter()2159            .filter(|i| i.owner == owner)2160            .next()2161            .ok_or(Error::<T>::NumOverflow)?;2162        let amount = item.fraction;21632164        ensure!(amount >= value, Error::<T>::TokenValueTooLow);21652166        // update balance2167        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2168            .checked_sub(value)2169            .ok_or(Error::<T>::NumOverflow)?;2170        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21712172        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2173            .checked_add(value)2174            .ok_or(Error::<T>::NumOverflow)?;2175        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21762177        let old_owner = item.owner.clone();2178        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21792180        // transfer2181        if amount == value && !new_owner_has_account {2182            // change owner2183            // new owner do not have account2184            let mut new_full_item = full_item.clone();2185            new_full_item2186                .owner2187                .iter_mut()2188                .find(|i| i.owner == owner)2189                .unwrap()2190                .owner = new_owner.clone();2191            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21922193            // update index collection2194            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2195        } else {2196            let mut new_full_item = full_item.clone();2197            new_full_item2198                .owner2199                .iter_mut()2200                .find(|i| i.owner == owner)2201                .unwrap()2202                .fraction -= value;22032204            // separate amount2205            if new_owner_has_account {2206                // new owner has account2207                new_full_item2208                    .owner2209                    .iter_mut()2210                    .find(|i| i.owner == new_owner)2211                    .unwrap()2212                    .fraction += value;2213            } else {2214                // new owner do not have account2215                new_full_item.owner.push(Ownership {2216                    owner: new_owner.clone(),2217                    fraction: value,2218                });2219                Self::add_token_index(collection_id, item_id, &new_owner)?;2220            }22212222            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2223        }22242225        Ok(())2226    }22272228    fn transfer_nft(2229        collection_id: CollectionId,2230        item_id: TokenId,2231        sender: T::AccountId,2232        new_owner: T::AccountId,2233    ) -> DispatchResult {2234        Self::token_exists(collection_id, item_id, &sender)?;22352236        let mut item = <NftItemList<T>>::get(collection_id, item_id);22372238        ensure!(2239            sender == item.owner,2240            Error::<T>::MustBeTokenOwner2241        );22422243        // update balance2244        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2245            .checked_sub(1)2246            .ok_or(Error::<T>::NumOverflow)?;2247        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22482249        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2250            .checked_add(1)2251            .ok_or(Error::<T>::NumOverflow)?;2252        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);22532254        // change owner2255        let old_owner = item.owner.clone();2256        item.owner = new_owner.clone();2257        <NftItemList<T>>::insert(collection_id, item_id, item);22582259        // update index collection2260        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22612262        Ok(())2263    }2264    2265    fn set_re_fungible_variable_data(2266        collection_id: CollectionId,2267        item_id: TokenId,2268        data: Vec<u8>2269    ) -> DispatchResult {2270        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);22712272        item.variable_data = data;22732274        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22752276        Ok(())2277    }22782279    fn set_nft_variable_data(2280        collection_id: CollectionId,2281        item_id: TokenId,2282        data: Vec<u8>2283    ) -> DispatchResult {2284        let mut item = <NftItemList<T>>::get(collection_id, item_id);2285        2286        item.variable_data = data;22872288        <NftItemList<T>>::insert(collection_id, item_id, item);2289        2290        Ok(())2291    }22922293    fn init_collection(item: &CollectionType<T::AccountId>) {2294        // check params2295        assert!(2296            item.decimal_points <= MAX_DECIMAL_POINTS,2297            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2298        );2299        assert!(2300            item.name.len() <= 64,2301            "Collection name can not be longer than 63 char"2302        );2303        assert!(2304            item.name.len() <= 256,2305            "Collection description can not be longer than 255 char"2306        );2307        assert!(2308            item.token_prefix.len() <= 16,2309            "Token prefix can not be longer than 15 char"2310        );23112312        // Generate next collection ID2313        let next_id = CreatedCollectionCount::get()2314            .checked_add(1)2315            .unwrap();23162317        CreatedCollectionCount::put(next_id);2318    }23192320    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2321        let current_index = <ItemListIndex>::get(collection_id)2322            .checked_add(1)2323            .unwrap();23242325        let item_owner = item.owner.clone();2326        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();23272328        <ItemListIndex>::insert(collection_id, current_index);23292330        // Update balance2331        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2332            .checked_add(1)2333            .unwrap();2334        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2335    }23362337    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2338        let current_index = <ItemListIndex>::get(collection_id)2339            .checked_add(1)2340            .unwrap();23412342        Self::add_token_index(collection_id, current_index, owner).unwrap();23432344        <ItemListIndex>::insert(collection_id, current_index);23452346        // Update balance2347        let new_balance = <Balance<T>>::get(collection_id, owner)2348            .checked_add(item.value)2349            .unwrap();2350        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2351    }23522353    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2354        let current_index = <ItemListIndex>::get(collection_id)2355            .checked_add(1)2356            .unwrap();23572358        let value = item.owner.first().unwrap().fraction;2359        let owner = item.owner.first().unwrap().owner.clone();23602361        Self::add_token_index(collection_id, current_index, &owner).unwrap();23622363        <ItemListIndex>::insert(collection_id, current_index);23642365        // Update balance2366        let new_balance = <Balance<T>>::get(collection_id, &owner)2367            .checked_add(value)2368            .unwrap();2369        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2370    }23712372    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {23732374        // add to account limit2375        if <AccountItemCount<T>>::contains_key(owner) {23762377            // bound Owned tokens by a single address2378            let count = <AccountItemCount<T>>::get(owner);2379            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23802381            <AccountItemCount<T>>::insert(owner.clone(), count2382                .checked_add(1)2383                .ok_or(Error::<T>::NumOverflow)?);2384        }2385        else {2386            <AccountItemCount<T>>::insert(owner.clone(), 1);2387        }23882389        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2390        if list_exists {2391            let mut list = <AddressTokens<T>>::get(collection_id, owner);2392            let item_contains = list.contains(&item_index.clone());23932394            if !item_contains {2395                list.push(item_index.clone());2396            }23972398            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2399        } else {2400            let mut itm = Vec::new();2401            itm.push(item_index.clone());2402            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2403        }24042405        Ok(())2406    }24072408    fn remove_token_index(2409        collection_id: CollectionId,2410        item_index: TokenId,2411        owner: &T::AccountId,2412    ) -> DispatchResult {24132414        // update counter2415        <AccountItemCount<T>>::insert(owner.clone(), 2416            <AccountItemCount<T>>::get(owner)2417            .checked_sub(1)2418            .ok_or(Error::<T>::NumOverflow)?);241924202421        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2422        if list_exists {2423            let mut list = <AddressTokens<T>>::get(collection_id, owner);2424            let item_contains = list.contains(&item_index.clone());24252426            if item_contains {2427                list.retain(|&item| item != item_index);2428                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2429            }2430        }24312432        Ok(())2433    }24342435    fn move_token_index(2436        collection_id: CollectionId,2437        item_index: TokenId,2438        old_owner: &T::AccountId,2439        new_owner: &T::AccountId,2440    ) -> DispatchResult {2441        Self::remove_token_index(collection_id, item_index, old_owner)?;2442        Self::add_token_index(collection_id, item_index, new_owner)?;24432444        Ok(())2445    }2446    2447    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2448        if <ContractOwner<T>>::contains_key(contract.clone()) {2449            let owner = <ContractOwner<T>>::get(contract);2450            ensure!(account == owner, Error::<T>::NoPermission);2451        } else {2452            fail!(Error::<T>::NoPermission);2453        }24542455        Ok(())2456    }2457}24582459////////////////////////////////////////////////////////////////////////////////////////////////////2460// Economic models2461// #region24622463/// Fee multiplier.2464pub type Multiplier = FixedU128;24652466type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24672468/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2469/// in the queue.2470#[derive(Encode, Decode, Clone, Eq, PartialEq)]2471pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);24722473impl<T: Config + Send + Sync> sp_std::fmt::Debug 2474    for ChargeTransactionPayment<T>2475{2476	#[cfg(feature = "std")]2477	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2478		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2479	}2480	#[cfg(not(feature = "std"))]2481	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2482		Ok(())2483	}2484}24852486impl<T: Config> ChargeTransactionPayment<T>2487where2488    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2489    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2490    T::AccountId: AsRef<[u8]>,2491    T::AccountId: UncheckedFrom<T::Hash>,2492{2493    fn traditional_fee(2494        len: usize,2495        info: &DispatchInfoOf<T::Call>,2496        tip: BalanceOf<T>,2497    ) -> BalanceOf<T>2498    where2499        T::Call: Dispatchable<Info = DispatchInfo>,2500    {2501        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2502    }25032504	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2505        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2506        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2507        let len_saturation = max_block_length as u64 / (len as u64).max(1);2508        let coefficient: BalanceOf<T> = weight_saturation2509            .min(len_saturation)2510            .saturated_into::<BalanceOf<T>>();2511        final_fee2512            .saturating_mul(coefficient)2513            .saturated_into::<TransactionPriority>()2514    }25152516    fn withdraw_fee(2517        &self,2518        who: &T::AccountId,2519        call: &T::Call,2520        info: &DispatchInfoOf<T::Call>,2521        len: usize,2522	) -> Result<2523		(2524			BalanceOf<T>,2525			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2526		),2527		TransactionValidityError,2528	> {2529        let tip = self.0;25302531        // Set fee based on call type. Creating collection costs 1 Unique.2532        // All other transactions have traditional fees so far2533        // let fee = match call.is_sub_type() {2534        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2535        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2536        //                                                 // _ => <BalanceOf<T>>::from(100)2537        // };2538        let fee = Self::traditional_fee(len, info, tip);25392540        // Only mess with balances if fee is not zero.2541        if fee.is_zero() {2542            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2543			.map(|i| (fee, i));2544        }25452546        // Determine who is paying transaction fee based on ecnomic model2547        // Parse call to extract collection ID and access collection sponsor2548        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2549            Some(Call::create_item(collection_id, _owner, _properties)) => {25502551                // sponsor timeout2552                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25532554                let collection = <Collection<T>>::get(collection_id);25552556                let limit = collection.limits.sponsor_transfer_timeout;2557                let mut sponsored = true;2558                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2559                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2560                    let limit_time = last_tx_block + limit.into();2561                    if block_number <= limit_time {2562                        sponsored = false;2563                    }2564                }2565                if sponsored {2566                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2567                }25682569                // check free create limit2570                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2571                   (sponsored)2572                {2573                    collection.sponsorship.sponsor()2574                        .cloned()2575                        .unwrap_or_default()2576                } else {2577                    T::AccountId::default()2578                }2579            }2580            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2581                2582                let mut sponsor_transfer = false;2583                if <Collection<T>>::get(collection_id).sponsorship.confirmed() {25842585                    let collection_limits = <Collection<T>>::get(collection_id).limits;2586                    let collection_mode = <Collection<T>>::get(collection_id).mode;2587    2588                    // sponsor timeout2589                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2590                    sponsor_transfer = match collection_mode {2591                        CollectionMode::NFT => {2592    2593                            // get correct limit2594                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2595                                collection_limits.sponsor_transfer_timeout2596                            } else {2597                                ChainLimit::get().nft_sponsor_transfer_timeout2598                            };2599    2600                            let mut sponsored = true;2601                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2602                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2603                                let limit_time = last_tx_block + limit.into();2604                                if block_number <= limit_time {2605                                    sponsored = false;2606                                }2607                            }2608                            if sponsored {2609                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2610                            }26112612                            sponsored2613                        }2614                        CollectionMode::Fungible(_) => {2615    2616                            // get correct limit2617                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2618                                collection_limits.sponsor_transfer_timeout2619                            } else {2620                                ChainLimit::get().fungible_sponsor_transfer_timeout2621                            };2622    2623                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2624                            let mut sponsored = true;2625                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2626                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2627                                let limit_time = last_tx_block + limit.into();2628                                if block_number <= limit_time {2629                                    sponsored = false;2630                                }2631                            }2632                            if sponsored {2633                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2634                            }26352636                            sponsored2637                        }2638                        CollectionMode::ReFungible => {2639    2640                            // get correct limit2641                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2642                                collection_limits.sponsor_transfer_timeout2643                            } else {2644                                ChainLimit::get().refungible_sponsor_transfer_timeout2645                            };2646    2647                            let mut sponsored = true;2648                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2649                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2650                                let limit_time = last_tx_block + limit.into();2651                                if block_number <= limit_time {2652                                    sponsored = false;2653                                }2654                            }2655                            if sponsored {2656                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2657                            }26582659                            sponsored2660                        }2661                        _ => {2662                            false2663                        },2664                    };2665                }26662667                if !sponsor_transfer {2668                    T::AccountId::default()2669                } else {2670                    <Collection<T>>::get(collection_id).sponsorship.sponsor()2671                        .cloned()2672                        .unwrap_or_default()2673                }2674            }26752676            _ => T::AccountId::default(),2677        };26782679        // Sponsor smart contracts2680        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26812682            // On instantiation: set the contract owner2683            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26842685                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2686                    &who,2687                    code_hash,2688                    salt,2689                );2690                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26912692                T::AccountId::default()2693            },26942695            // On instantiation with code: set the contract owner2696            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {26972698                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2699                    &who,2700                    &T::Hashing::hash(&_code),2701                    _salt,2702                );27032704                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());27052706                T::AccountId::default()2707            }27082709            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2710            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27112712                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27132714                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2715                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2716                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2717                  2718                if !owned_contract && white_list_enabled {2719                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2720                        return Err(InvalidTransaction::Call.into());2721                    }2722                }27232724                let mut sponsor_transfer = false;2725                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2726                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2727                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2728                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2729                    let limit_time = last_tx_block + rate_limit;27302731                    if block_number >= limit_time {2732                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2733                        sponsor_transfer = true;2734                    }2735                } else {2736                    sponsor_transfer = false;2737                }2738               2739                2740                let mut sp = T::AccountId::default();2741                if sponsor_transfer {2742                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2743                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2744                            sp = called_contract;2745                        }2746                    }2747                }27482749                sp2750            },27512752            _ => sponsor,2753        };27542755        let mut who_pays_fee: T::AccountId = sponsor.clone();2756        if sponsor == T::AccountId::default() {2757            who_pays_fee = who.clone();2758        }27592760		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2761			.map(|i| (fee, i))2762    }2763}276427652766impl<T: Config + Send + Sync> SignedExtension2767    for ChargeTransactionPayment<T>2768where2769    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2770    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2771    T::AccountId: AsRef<[u8]>,2772    T::AccountId: UncheckedFrom<T::Hash>,2773{2774    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2775    type AccountId = T::AccountId;2776    type Call = T::Call;2777    type AdditionalSigned = ();2778    type Pre = (2779        // tip2780        BalanceOf<T>,2781        // who pays fee2782        Self::AccountId,2783		// imbalance resulting from withdrawing the fee2784		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2785    );2786    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2787        Ok(())2788    }27892790    fn validate(2791        &self,2792        who: &Self::AccountId,2793        call: &Self::Call,2794        info: &DispatchInfoOf<Self::Call>,2795        len: usize,2796    ) -> TransactionValidity {2797		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2798		Ok(ValidTransaction {2799			priority: Self::get_priority(len, info, fee),2800			..Default::default()2801		})2802    }28032804    fn pre_dispatch(2805        self,2806        who: &Self::AccountId,2807        call: &Self::Call,2808        info: &DispatchInfoOf<Self::Call>,2809        len: usize,2810    ) -> Result<Self::Pre, TransactionValidityError> {2811        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2812        Ok((self.0, who.clone(), imbalance))2813    }28142815    fn post_dispatch(2816        pre: Self::Pre,2817        info: &DispatchInfoOf<Self::Call>,2818        post_info: &PostDispatchInfoOf<Self::Call>,2819        len: usize,2820        _result: &DispatchResult,2821    ) -> Result<(), TransactionValidityError> {2822		let (tip, who, imbalance) = pre;2823		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2824			len as u32,2825			info,2826			post_info,2827			tip,2828		);2829		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2830		Ok(())2831    }2832}28332834// #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    transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_runtime::sp_std::prelude::Vec;36use sp_runtime::{37    traits::{38        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,39    },40    transaction_validity::{41        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,42    },43    FixedPointOperand, FixedU128,44};45use sp_runtime::traits::StaticLookup;46use pallet_contracts::chain_extension::UncheckedFrom;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;5657pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;58pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;59pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;60pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6162// Structs63// #region6465pub type CollectionId = u32;66pub type TokenId = u32;67pub type DecimalPoints = u8;6869#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]70#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]71pub enum CollectionMode {72    Invalid,73    NFT,74    // decimal points75    Fungible(DecimalPoints),76    ReFungible,77}7879impl Default for CollectionMode {80    fn default() -> Self {81        Self::Invalid82    }83}8485impl Into<u8> for CollectionMode {86    fn into(self) -> u8 {87        match self {88            CollectionMode::Invalid => 0,89            CollectionMode::NFT => 1,90            CollectionMode::Fungible(_) => 2,91            CollectionMode::ReFungible => 3,92        }93    }94}9596#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]97#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]98pub enum AccessMode {99    Normal,100    WhiteList,101}102impl Default for AccessMode {103    fn default() -> Self {104        Self::Normal105    }106}107108#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]109#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]110pub enum SchemaVersion {111    ImageURL,112    Unique,113}114impl Default for SchemaVersion {115    fn default() -> Self {116        Self::ImageURL117    }118}119120#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]121#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]122pub struct Ownership<AccountId> {123    pub owner: AccountId,124    pub fraction: u128,125}126127#[derive(Encode, Decode, Debug, Clone, PartialEq)]128#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]129pub enum SponsorshipState<AccountId> {130    /// The fees are applied to the transaction sender131    Disabled,132    Unconfirmed(AccountId),133    /// Transactions are sponsored by specified account134    Confirmed(AccountId),135}136137impl<AccountId> SponsorshipState<AccountId> {138    fn sponsor(&self) -> Option<&AccountId> {139        match self {140            Self::Confirmed(sponsor) => Some(sponsor),141            _ => None,142        }143    }144145    fn pending_sponsor(&self) -> Option<&AccountId> {146        match self {147            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),148            _ => None,149        }150    }151152    fn confirmed(&self) -> bool {153        matches!(self, Self::Confirmed(_))154    }155}156157impl<T> Default for SponsorshipState<T> {158    fn default() -> Self {159        Self::Disabled160    }161}162163#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]164#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]165pub struct CollectionType<AccountId> {166    pub owner: AccountId,167    pub mode: CollectionMode,168    pub access: AccessMode,169    pub decimal_points: DecimalPoints,170    pub name: Vec<u16>,        // 64 include null escape char171    pub description: Vec<u16>, // 256 include null escape char172    pub token_prefix: Vec<u8>, // 16 include null escape char173    pub mint_mode: bool,174    pub offchain_schema: Vec<u8>,175    pub schema_version: SchemaVersion,176    pub sponsorship: SponsorshipState<AccountId>,177    pub limits: CollectionLimits, // Collection private restrictions 178    pub variable_on_chain_schema: Vec<u8>, //179    pub const_on_chain_schema: Vec<u8>, //180}181182#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]183#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]184pub struct NftItemType<AccountId> {185    pub owner: AccountId,186    pub const_data: Vec<u8>,187    pub variable_data: Vec<u8>,188}189190#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]191#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]192pub struct FungibleItemType {193    pub value: u128,194}195196#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]197#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]198pub struct ReFungibleItemType<AccountId> {199    pub owner: Vec<Ownership<AccountId>>,200    pub const_data: Vec<u8>,201    pub variable_data: Vec<u8>,202}203204// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]205// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]206// pub struct VestingItem<AccountId, Moment> {207//     pub sender: AccountId,208//     pub recipient: AccountId,209//     pub collection_id: CollectionId,210//     pub item_id: TokenId,211//     pub amount: u64,212//     pub vesting_date: Moment,213// }214215#[derive(Encode, Decode, Debug, Clone, PartialEq)]216#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]217pub struct CollectionLimits {218    pub account_token_ownership_limit: u32,219    pub sponsored_data_size: u32,220    pub token_limit: u32,221222    // Timeouts for item types in passed blocks223    pub sponsor_transfer_timeout: u32,224    pub owner_can_transfer: bool,225    pub owner_can_destroy: bool,226}227228impl Default for CollectionLimits {229    fn default() -> CollectionLimits {230        CollectionLimits { 231            account_token_ownership_limit: 10_000_000, 232            token_limit: u32::max_value(),233            sponsored_data_size: u32::MAX,234            sponsor_transfer_timeout: 14400,235            owner_can_transfer: true,236            owner_can_destroy: true237        }238    }239}240241#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]242#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]243pub struct ChainLimits {244    pub collection_numbers_limit: u32,245    pub account_token_ownership_limit: u32,246    pub collections_admins_limit: u64,247    pub custom_data_limit: u32,248249    // Timeouts for item types in passed blocks250    pub nft_sponsor_transfer_timeout: u32,251    pub fungible_sponsor_transfer_timeout: u32,252    pub refungible_sponsor_transfer_timeout: u32,253254    // Schema limits255    pub offchain_schema_limit: u32,256    pub variable_on_chain_schema_limit: u32,257    pub const_on_chain_schema_limit: u32,258}259260pub trait WeightInfo {261	fn create_collection() -> Weight;262	fn destroy_collection() -> Weight;263	fn add_to_white_list() -> Weight;264	fn remove_from_white_list() -> Weight;265    fn set_public_access_mode() -> Weight;266    fn set_mint_permission() -> Weight;267    fn change_collection_owner() -> Weight;268    fn add_collection_admin() -> Weight;269    fn remove_collection_admin() -> Weight;270    fn set_collection_sponsor() -> Weight;271    fn confirm_sponsorship() -> Weight;272    fn remove_collection_sponsor() -> Weight;273    fn create_item(s: usize) -> Weight;274    fn burn_item() -> Weight;275    fn transfer() -> Weight;276    fn approve() -> Weight;277    fn transfer_from() -> Weight;278    fn set_offchain_schema() -> Weight;279    fn set_const_on_chain_schema() -> Weight;280    fn set_variable_on_chain_schema() -> Weight;281    fn set_variable_meta_data() -> Weight;282    fn enable_contract_sponsoring() -> Weight;283    fn set_schema_version() -> Weight;284    fn set_chain_limits() -> Weight;285    fn set_contract_sponsoring_rate_limit() -> Weight;286    fn toggle_contract_white_list() -> Weight;287    fn add_to_contract_white_list() -> Weight;288    fn remove_from_contract_white_list() -> Weight;289    fn set_collection_limits() -> Weight;290}291292#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]293#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]294pub struct CreateNftData {295    pub const_data: Vec<u8>,296    pub variable_data: Vec<u8>,297}298299#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]300#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]301pub struct CreateFungibleData {302    pub value: u128,303}304305#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]306#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]307pub struct CreateReFungibleData {308    pub const_data: Vec<u8>,309    pub variable_data: Vec<u8>,310    pub pieces: u128,311}312313#[derive(Encode, Decode, Debug, Clone, PartialEq)]314#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]315pub enum CreateItemData {316    NFT(CreateNftData),317    Fungible(CreateFungibleData),318    ReFungible(CreateReFungibleData),319}320321impl CreateItemData {322    pub fn len(&self) -> usize {323        let len = match self {324            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),325            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),326            _ => 0327        };328        329        return len;330    }331}332333impl From<CreateNftData> for CreateItemData {334    fn from(item: CreateNftData) -> Self {335        CreateItemData::NFT(item)336    }337}338339impl From<CreateReFungibleData> for CreateItemData {340    fn from(item: CreateReFungibleData) -> Self {341        CreateItemData::ReFungible(item)342    }343}344345impl From<CreateFungibleData> for CreateItemData {346    fn from(item: CreateFungibleData) -> Self {347        CreateItemData::Fungible(item)348    }349}350351352decl_error! {353	/// Error for non-fungible-token module.354	pub enum Error for Module<T: Config> {355        /// Total collections bound exceeded.356        TotalCollectionsLimitExceeded,357		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.358        CollectionDecimalPointLimitExceeded, 359        /// Collection name can not be longer than 63 char.360        CollectionNameLimitExceeded, 361        /// Collection description can not be longer than 255 char.362        CollectionDescriptionLimitExceeded, 363        /// Token prefix can not be longer than 15 char.364        CollectionTokenPrefixLimitExceeded,365        /// This collection does not exist.366        CollectionNotFound,367        /// Item not exists.368        TokenNotFound,369        /// Admin not found370        AdminNotFound,371        /// Arithmetic calculation overflow.372        NumOverflow,       373        /// Account already has admin role.374        AlreadyAdmin,  375        /// You do not own this collection.376        NoPermission,377        /// This address is not set as sponsor, use setCollectionSponsor first.378        ConfirmUnsetSponsorFail,379        /// Collection is not in mint mode.380        PublicMintingNotAllowed,381        /// Sender parameter and item owner must be equal.382        MustBeTokenOwner,383        /// Item balance not enough.384        TokenValueTooLow,385        /// Size of item is too large.386        NftSizeLimitExceeded,387        /// No approve found388        ApproveNotFound,389        /// Requested value more than approved.390        TokenValueNotEnough,391        /// Only approved addresses can call this method.392        ApproveRequired,393        /// Address is not in white list.394        AddresNotInWhiteList,395        /// Number of collection admins bound exceeded.396        CollectionAdminsLimitExceeded,397        /// Owned tokens by a single address bound exceeded.398        AddressOwnershipLimitExceeded,399        /// Length of items properties must be greater than 0.400        EmptyArgument,401        /// const_data exceeded data limit.402        TokenConstDataLimitExceeded,403        /// variable_data exceeded data limit.404        TokenVariableDataLimitExceeded,405        /// Not NFT item data used to mint in NFT collection.406        NotNftDataUsedToMintNftCollectionToken,407        /// Not Fungible item data used to mint in Fungible collection.408        NotFungibleDataUsedToMintFungibleCollectionToken,409        /// Not Re Fungible item data used to mint in Re Fungible collection.410        NotReFungibleDataUsedToMintReFungibleCollectionToken,411        /// Unexpected collection type.412        UnexpectedCollectionType,413        /// Can't store metadata in fungible tokens.414        CantStoreMetadataInFungibleTokens,415        /// Collection token limit exceeded416        CollectionTokenLimitExceeded,417        /// Account token limit exceeded per collection418        AccountTokenLimitExceeded,419        /// Collection limit bounds per collection exceeded420        CollectionLimitBoundsExceeded,421        /// Tried to enable permissions which are only permitted to be disabled422        OwnerPermissionsCantBeReverted,423        /// Schema data size limit bound exceeded424        SchemaDataLimitExceeded,425        /// Maximum refungibility exceeded426        WrongRefungiblePieces,427        /// createRefungible should be called with one owner428        BadCreateRefungibleCall,429	}430}431432pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {433    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;434435    /// Weight information for extrinsics in this pallet.436	type WeightInfo: WeightInfo;437}438439#[cfg(feature = "runtime-benchmarks")]440mod benchmarking;441442// #endregion443444// # Used definitions445//446// ## User control levels447//448// chain-controlled - key is uncontrolled by user449//                    i.e autoincrementing index450//                    can use non-cryptographic hash451// real - key is controlled by user452//        but it is hard to generate enough colliding values, i.e owner of signed txs453//        can use non-cryptographic hash454// controlled - key is completly controlled by users455//              i.e maps with mutable keys456//              should use cryptographic hash457//458// ## User control level downgrade reasons459//460// ?1 - chain-controlled -> controlled461//      collections/tokens can be destroyed, resulting in massive holes462// ?2 - chain-controlled -> controlled463//      same as ?1, but can be only added, resulting in easier exploitation464// ?3 - real -> controlled465//      no confirmation required, so addresses can be easily generated466decl_storage! {467    trait Store for Module<T: Config> as Nft {468469        //#region Private members470        /// Id of next collection471        CreatedCollectionCount: u32;472        /// Used for migrations473        ChainVersion: u64;474        /// Id of last collection token475        /// Collection id (controlled?1)476        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;477        //#endregion478479        //#region Chain limits struct480        pub ChainLimit get(fn chain_limit) config(): ChainLimits;481        //#endregion482483        //#region Bound counters484        /// Amount of collections destroyed, used for total amount tracking with485        /// CreatedCollectionCount486        DestroyedCollectionCount: u32;487        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)488        /// Account id (real)489        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;490        //#endregion491492        //#region Basic collections493        /// Collection info494        /// Collection id (controlled?1)495        pub Collection get(fn collection) config(): map hasher(blake2_128_concat) CollectionId => CollectionType<T::AccountId>;496        /// List of collection admins497        /// Collection id (controlled?2)498        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::AccountId>;499        /// Whitelisted collection users500        /// Collection id (controlled?2), user id (controlled?3)501        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;502        //#endregion503504        /// How many of collection items user have505        /// Collection id (controlled?2), account id (real)506        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;507508        /// Amount of items which spender can transfer out of owners account (via transferFrom)509        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))510        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;511512        //#region Item collections513        /// Collection id (controlled?2), token id (controlled?1)514        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => NftItemType<T::AccountId>;515        /// Collection id (controlled?2), owner (controlled?2)516        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;517        /// Collection id (controlled?2), token id (controlled?1)518        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => ReFungibleItemType<T::AccountId>;519        //#endregion520521        //#region Index list522        /// Collection id (controlled?2), tokens owner (controlled?2)523        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;524        //#endregion525526        //#region Tokens transfer rate limit baskets527        /// (Collection id (controlled?2), who created (real))528        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;529        /// Collection id (controlled?2), token id (controlled?2)530        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;531        /// Collection id (controlled?2), owning user (real)532        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;533        /// Collection id (controlled?2), token id (controlled?2)534        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;535        //#endregion536537        //#region Contract Sponsorship and Ownership538        /// Contract address (real)539        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;540        /// Contract address (real)541        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;542        /// (Contract address(real), caller (real))543        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;544        /// Contract address (real)545        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;546        /// Contract address (real)547        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 548        /// Contract address (real) => Whitelisted user (controlled?3)549        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 550        //#endregion551    }552    add_extra_genesis {553        build(|config: &GenesisConfig<T>| {554            // Modification of storage555            for (_num, _c) in &config.collection {556                <Module<T>>::init_collection(_c);557            }558559            for (_num, _c, _i) in &config.nft_item_id {560                <Module<T>>::init_nft_token(*_c, _i);561            }562563            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {564                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);565            }566567            for (_num, _c, _i) in &config.refungible_item_id {568                <Module<T>>::init_refungible_token(*_c, _i);569            }570        })571    }572}573574decl_event!(575    pub enum Event<T>576    where577        AccountId = <T as system::Config>::AccountId,578    {579        /// New collection was created580        /// 581        /// # Arguments582        /// 583        /// * collection_id: Globally unique identifier of newly created collection.584        /// 585        /// * mode: [CollectionMode] converted into u8.586        /// 587        /// * account_id: Collection owner.588        Created(CollectionId, u8, AccountId),589590        /// New item was created.591        /// 592        /// # Arguments593        /// 594        /// * collection_id: Id of the collection where item was created.595        /// 596        /// * item_id: Id of an item. Unique within the collection.597        ///598        /// * recipient: Owner of newly created item 599        ItemCreated(CollectionId, TokenId, AccountId),600601        /// Collection item was burned.602        /// 603        /// # Arguments604        /// 605        /// collection_id.606        /// 607        /// item_id: Identifier of burned NFT.608        ItemDestroyed(CollectionId, TokenId),609610        /// Item was transferred611        ///612        /// * collection_id: Id of collection to which item is belong613        ///614        /// * item_id: Id of an item615        ///616        /// * sender: Original owner of item617        ///618        /// * recipient: New owner of item619        ///620        /// * amount: Always 1 for NFT621        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),622    }623);624625decl_module! {626    pub struct Module<T: Config> for enum Call 627    where 628        origin: T::Origin629    {630        fn deposit_event() = default;631        type Error = Error<T>;632633        fn on_initialize(now: T::BlockNumber) -> Weight {634            0635        }636637        /// 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.638        /// 639        /// # Permissions640        /// 641        /// * Anyone.642        /// 643        /// # Arguments644        /// 645        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.646        /// 647        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.648        /// 649        /// * token_prefix: UTF-8 string with token prefix.650        /// 651        /// * mode: [CollectionMode] collection type and type dependent data.652        // returns collection ID653        #[weight = <T as Config>::WeightInfo::create_collection()]654        #[transactional]655        pub fn create_collection(origin,656                                 collection_name: Vec<u16>,657                                 collection_description: Vec<u16>,658                                 token_prefix: Vec<u8>,659                                 mode: CollectionMode) -> DispatchResult {660661            // Anyone can create a collection662            let who = ensure_signed(origin)?;663664            let decimal_points = match mode {665                CollectionMode::Fungible(points) => points,666                _ => 0667            };668669            let chain_limit = ChainLimit::get();670671            let created_count = CreatedCollectionCount::get();672            let destroyed_count = DestroyedCollectionCount::get();673674            // bound Total number of collections675            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);676677            // check params678            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);679            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);680            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);681            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);682683            // Generate next collection ID684            let next_id = created_count685                .checked_add(1)686                .ok_or(Error::<T>::NumOverflow)?;687688            CreatedCollectionCount::put(next_id);689690            let limits = CollectionLimits {691                sponsored_data_size: chain_limit.custom_data_limit,692                ..Default::default()693            };694695            // Create new collection696            let new_collection = CollectionType {697                owner: who.clone(),698                name: collection_name,699                mode: mode.clone(),700                mint_mode: false,701                access: AccessMode::Normal,702                description: collection_description,703                decimal_points: decimal_points,704                token_prefix: token_prefix,705                offchain_schema: Vec::new(),706                schema_version: SchemaVersion::ImageURL,707                sponsorship: SponsorshipState::Disabled,708                variable_on_chain_schema: Vec::new(),709                const_on_chain_schema: Vec::new(),710                limits,711            };712713            // Add new collection to map714            <Collection<T>>::insert(next_id, new_collection);715716            // call event717            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));718719            Ok(())720        }721722        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.723        /// 724        /// # Permissions725        /// 726        /// * Collection Owner.727        /// 728        /// # Arguments729        /// 730        /// * collection_id: collection to destroy.731        #[weight = <T as Config>::WeightInfo::destroy_collection()]732        #[transactional]733        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {734735            let sender = ensure_signed(origin)?;736            Self::check_owner_permissions(collection_id, sender)?;737738            let target_collection = <Collection<T>>::get(collection_id);739            if !target_collection.limits.owner_can_destroy {740                fail!(Error::<T>::NoPermission);741            }742743            <AddressTokens<T>>::remove_prefix(collection_id);744            <Allowances<T>>::remove_prefix(collection_id);745            <Balance<T>>::remove_prefix(collection_id);746            <ItemListIndex>::remove(collection_id);747            <AdminList<T>>::remove(collection_id);748            <Collection<T>>::remove(collection_id);749            <WhiteList<T>>::remove_prefix(collection_id);750751            <NftItemList<T>>::remove_prefix(collection_id);752            <FungibleItemList<T>>::remove_prefix(collection_id);753            <ReFungibleItemList<T>>::remove_prefix(collection_id);754755            <NftTransferBasket<T>>::remove_prefix(collection_id);756            <FungibleTransferBasket<T>>::remove_prefix(collection_id);757            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);758759            DestroyedCollectionCount::put(DestroyedCollectionCount::get()760                .checked_add(1)761                .ok_or(Error::<T>::NumOverflow)?);762763            Ok(())764        }765766        /// Add an address to white list.767        /// 768        /// # Permissions769        /// 770        /// * Collection Owner771        /// * Collection Admin772        /// 773        /// # Arguments774        /// 775        /// * collection_id.776        /// 777        /// * address.778        #[weight = <T as Config>::WeightInfo::add_to_white_list()]779        #[transactional]780        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{781782            let sender = ensure_signed(origin)?;783            Self::check_owner_or_admin_permissions(collection_id, sender)?;784785            <WhiteList<T>>::insert(collection_id, address, true);786            787            Ok(())788        }789790        /// Remove an address from white list.791        /// 792        /// # Permissions793        /// 794        /// * Collection Owner795        /// * Collection Admin796        /// 797        /// # Arguments798        /// 799        /// * collection_id.800        /// 801        /// * address.802        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]803        #[transactional]804        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{805806            let sender = ensure_signed(origin)?;807            Self::check_owner_or_admin_permissions(collection_id, sender)?;808809            <WhiteList<T>>::remove(collection_id, address);810811            Ok(())812        }813814        /// Toggle between normal and white list access for the methods with access for `Anyone`.815        /// 816        /// # Permissions817        /// 818        /// * Collection Owner.819        /// 820        /// # Arguments821        /// 822        /// * collection_id.823        /// 824        /// * mode: [AccessMode]825        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]826        #[transactional]827        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult828        {829            let sender = ensure_signed(origin)?;830831            Self::check_owner_permissions(collection_id, sender)?;832            let mut target_collection = <Collection<T>>::get(collection_id);833            target_collection.access = mode;834            <Collection<T>>::insert(collection_id, target_collection);835836            Ok(())837        }838839        /// Allows Anyone to create tokens if:840        /// * White List is enabled, and841        /// * Address is added to white list, and842        /// * This method was called with True parameter843        /// 844        /// # Permissions845        /// * Collection Owner846        ///847        /// # Arguments848        /// 849        /// * collection_id.850        /// 851        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.852        #[weight = <T as Config>::WeightInfo::set_mint_permission()]853        #[transactional]854        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult855        {856            let sender = ensure_signed(origin)?;857858            Self::check_owner_permissions(collection_id, sender)?;859            let mut target_collection = <Collection<T>>::get(collection_id);860            target_collection.mint_mode = mint_permission;861            <Collection<T>>::insert(collection_id, target_collection);862863            Ok(())864        }865866        /// Change the owner of the collection.867        /// 868        /// # Permissions869        /// 870        /// * Collection Owner.871        /// 872        /// # Arguments873        /// 874        /// * collection_id.875        /// 876        /// * new_owner.877        #[weight = <T as Config>::WeightInfo::change_collection_owner()]878        #[transactional]879        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {880881            let sender = ensure_signed(origin)?;882            Self::check_owner_permissions(collection_id, sender)?;883            let mut target_collection = <Collection<T>>::get(collection_id);884            target_collection.owner = new_owner;885            <Collection<T>>::insert(collection_id, target_collection);886887            Ok(())888        }889890        /// Adds an admin of the Collection.891        /// 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. 892        /// 893        /// # Permissions894        /// 895        /// * Collection Owner.896        /// * Collection Admin.897        /// 898        /// # Arguments899        /// 900        /// * collection_id: ID of the Collection to add admin for.901        /// 902        /// * new_admin_id: Address of new admin to add.903        #[weight = <T as Config>::WeightInfo::add_collection_admin()]904        #[transactional]905        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {906907            let sender = ensure_signed(origin)?;908            Self::check_owner_or_admin_permissions(collection_id, sender)?;909            let mut admin_arr: Vec<T::AccountId> = Vec::new();910911            if <AdminList<T>>::contains_key(collection_id)912            {913                admin_arr = <AdminList<T>>::get(collection_id);914                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);915            }916917            // Number of collection admins918            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);919920            admin_arr.push(new_admin_id);921            <AdminList<T>>::insert(collection_id, admin_arr);922923            Ok(())924        }925926        /// 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.927        ///928        /// # Permissions929        /// 930        /// * Collection Owner.931        /// * Collection Admin.932        /// 933        /// # Arguments934        /// 935        /// * collection_id: ID of the Collection to remove admin for.936        /// 937        /// * account_id: Address of admin to remove.938        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]939        #[transactional]940        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {941942            let sender = ensure_signed(origin)?;943            Self::check_owner_or_admin_permissions(collection_id, sender)?;944            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);945946            let mut admin_arr = <AdminList<T>>::get(collection_id);947            admin_arr.retain(|i| *i != account_id);948            <AdminList<T>>::insert(collection_id, admin_arr);949950            Ok(())951        }952953        /// # Permissions954        /// 955        /// * Collection Owner956        /// 957        /// # Arguments958        /// 959        /// * collection_id.960        /// 961        /// * new_sponsor.962        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]963        #[transactional]964        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> 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.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);973            <Collection<T>>::insert(collection_id, target_collection);974975            Ok(())976        }977978        /// # Permissions979        /// 980        /// * Sponsor.981        /// 982        /// # Arguments983        /// 984        /// * collection_id.985        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]986        #[transactional]987        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {988989            let sender = ensure_signed(origin)?;990            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);991992            let mut target_collection = <Collection<T>>::get(collection_id);993            ensure!(994                target_collection.sponsorship.pending_sponsor() == Some(&sender),995                Error::<T>::ConfirmUnsetSponsorFail996            );997998            target_collection.sponsorship = SponsorshipState::Confirmed(sender);999            <Collection<T>>::insert(collection_id, target_collection);10001001            Ok(())1002        }10031004        /// Switch back to pay-per-own-transaction model.1005        ///1006        /// # Permissions1007        ///1008        /// * Collection owner.1009        /// 1010        /// # Arguments1011        /// 1012        /// * collection_id.1013        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1014        #[transactional]1015        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {10161017            let sender = ensure_signed(origin)?;1018            ensure!(<Collection<T>>::contains_key(collection_id), Error::<T>::CollectionNotFound);10191020            let mut target_collection = <Collection<T>>::get(collection_id);1021            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);10221023            target_collection.sponsorship = SponsorshipState::Disabled;1024            <Collection<T>>::insert(collection_id, target_collection);10251026            Ok(())1027        }10281029        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1030        /// 1031        /// # Permissions1032        /// 1033        /// * Collection Owner.1034        /// * Collection Admin.1035        /// * Anyone if1036        ///     * White List is enabled, and1037        ///     * Address is added to white list, and1038        ///     * MintPermission is enabled (see SetMintPermission method)1039        /// 1040        /// # Arguments1041        /// 1042        /// * collection_id: ID of the collection.1043        /// 1044        /// * owner: Address, initial owner of the NFT.1045        ///1046        /// * data: Token data to store on chain.1047        // #[weight =1048        // (130_000_000 as Weight)1049        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1050        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1051        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]10521053        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1054        #[transactional]1055        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {10561057            let sender = ensure_signed(origin)?;10581059            Self::collection_exists(collection_id)?;10601061            let target_collection = <Collection<T>>::get(collection_id);10621063            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;1064            Self::validate_create_item_args(&target_collection, &data)?;1065            Self::create_item_no_validation(collection_id, owner, data)?;10661067            Ok(())1068        }10691070        /// This method creates multiple items in a collection created with CreateCollection method.1071        /// 1072        /// # Permissions1073        /// 1074        /// * Collection Owner.1075        /// * Collection Admin.1076        /// * Anyone if1077        ///     * White List is enabled, and1078        ///     * Address is added to white list, and1079        ///     * MintPermission is enabled (see SetMintPermission method)1080        /// 1081        /// # Arguments1082        /// 1083        /// * collection_id: ID of the collection.1084        /// 1085        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1086        /// 1087        /// * owner: Address, initial owner of the NFT.1088        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1089                               .map(|data| { data.len() })1090                               .sum())]1091        #[transactional]1092        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10931094            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1095            let sender = ensure_signed(origin)?;10961097            Self::collection_exists(collection_id)?;1098            let target_collection = <Collection<T>>::get(collection_id);10991100            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;11011102            for data in &items_data {1103                Self::validate_create_item_args(&target_collection, data)?;1104            }1105            for data in &items_data {1106                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1107            }11081109            Ok(())1110        }11111112        /// Destroys a concrete instance of NFT.1113        /// 1114        /// # Permissions1115        /// 1116        /// * Collection Owner.1117        /// * Collection Admin.1118        /// * Current NFT Owner.1119        /// 1120        /// # Arguments1121        /// 1122        /// * collection_id: ID of the collection.1123        /// 1124        /// * item_id: ID of NFT to burn.1125        #[weight = <T as Config>::WeightInfo::burn_item()]1126        #[transactional]1127        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {11281129            let sender = ensure_signed(origin)?;1130            Self::collection_exists(collection_id)?;11311132            // Transfer permissions check1133            let target_collection = <Collection<T>>::get(collection_id);1134            ensure!(1135                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1136                (1137                    target_collection.limits.owner_can_transfer &&1138                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1139                ),1140                Error::<T>::NoPermission1141            );11421143            if target_collection.access == AccessMode::WhiteList {1144                Self::check_white_list(collection_id, &sender)?;1145            }11461147            match target_collection.mode1148            {1149                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1150                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1151                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1152                _ => ()1153            };11541155            // call event1156            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));11571158            Ok(())1159        }11601161        /// Change ownership of the token.1162        /// 1163        /// # Permissions1164        /// 1165        /// * Collection Owner1166        /// * Collection Admin1167        /// * Current NFT owner1168        ///1169        /// # Arguments1170        /// 1171        /// * recipient: Address of token recipient.1172        /// 1173        /// * collection_id.1174        /// 1175        /// * item_id: ID of the item1176        ///     * Non-Fungible Mode: Required.1177        ///     * Fungible Mode: Ignored.1178        ///     * Re-Fungible Mode: Required.1179        /// 1180        /// * value: Amount to transfer.1181        ///     * Non-Fungible Mode: Ignored1182        ///     * Fungible Mode: Must specify transferred amount1183        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1184        #[weight = <T as Config>::WeightInfo::transfer()]1185        #[transactional]1186        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1187            let sender = ensure_signed(origin)?;1188            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1189        }11901191        /// Set, change, or remove approved address to transfer the ownership of the NFT.1192        /// 1193        /// # Permissions1194        /// 1195        /// * Collection Owner1196        /// * Collection Admin1197        /// * Current NFT owner1198        /// 1199        /// # Arguments1200        /// 1201        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1202        /// 1203        /// * collection_id.1204        /// 1205        /// * item_id: ID of the item.1206        #[weight = <T as Config>::WeightInfo::approve()]1207        #[transactional]1208        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {12091210            let sender = ensure_signed(origin)?;12111212            Self::collection_exists(collection_id)?;1213            Self::token_exists(collection_id, item_id, &sender)?;12141215            // Transfer permissions check1216            let target_collection = <Collection<T>>::get(collection_id);1217            let allowance_limit = if target_collection.limits.owner_can_transfer &&1218                Self::is_owner_or_admin_permissions(1219                    collection_id,1220                    sender.clone(),1221                ) {1222                None1223            } else if let Some(amount) = Self::owned_amount(1224                sender.clone(),1225                collection_id,1226                item_id,1227            ) {1228                Some(amount)1229            } else {1230                fail!(Error::<T>::NoPermission);1231            };12321233            if target_collection.access == AccessMode::WhiteList {1234                Self::check_white_list(collection_id, &sender)?;1235                Self::check_white_list(collection_id, &spender)?;1236            }12371238            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1239            let mut allowance: u128 = amount;1240            if allowance_exists {1241                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1242            }1243            if let Some(limit) = allowance_limit {1244                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1245            }1246            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);12471248            Ok(())1249        }1250        1251        /// 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.1252        /// 1253        /// # Permissions1254        /// * Collection Owner1255        /// * Collection Admin1256        /// * Current NFT owner1257        /// * Address approved by current NFT owner1258        /// 1259        /// # Arguments1260        /// 1261        /// * from: Address that owns token.1262        /// 1263        /// * recipient: Address of token recipient.1264        /// 1265        /// * collection_id.1266        /// 1267        /// * item_id: ID of the item.1268        /// 1269        /// * value: Amount to transfer.1270        #[weight = <T as Config>::WeightInfo::transfer_from()]1271        #[transactional]1272        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {12731274            let sender = ensure_signed(origin)?;1275            let mut appoved_transfer = false;12761277            // Check approval1278            let mut approval: u128 = 0;1279            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1280                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1281                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1282                appoved_transfer = true;1283            }12841285            let target_collection = <Collection<T>>::get(collection_id);12861287            // Limits check1288            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12891290            // Transfer permissions check         1291            ensure!(1292                appoved_transfer || 1293                (1294                    target_collection.limits.owner_can_transfer &&1295                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1296                ),1297                Error::<T>::NoPermission1298            );12991300            if target_collection.access == AccessMode::WhiteList {1301                Self::check_white_list(collection_id, &sender)?;1302                Self::check_white_list(collection_id, &recipient)?;1303            }13041305            // Reduce approval by transferred amount or remove if remaining approval drops to 01306            if approval.saturating_sub(value) > 0 {1307                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1308            }1309            else {1310                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1311            }13121313            match target_collection.mode1314            {1315                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1316                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1317                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1318                _ => ()1319            };13201321            Ok(())1322        }13231324        // #[weight = 0]1325        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {13261327        //     // let no_perm_mes = "You do not have permissions to modify this collection";1328        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1329        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1330        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13311332        //     // // on_nft_received  call13331334        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13351336        //     Ok(())1337        // }13381339        /// Set off-chain data schema.1340        /// 1341        /// # Permissions1342        /// 1343        /// * Collection Owner1344        /// * Collection Admin1345        /// 1346        /// # Arguments1347        /// 1348        /// * collection_id.1349        /// 1350        /// * schema: String representing the offchain data schema.1351        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1352        #[transactional]1353        pub fn set_variable_meta_data (1354            origin,1355            collection_id: CollectionId,1356            item_id: TokenId,1357            data: Vec<u8>1358        ) -> DispatchResult {1359            let sender = ensure_signed(origin)?;1360            1361            Self::collection_exists(collection_id)?;1362            Self::token_exists(collection_id, item_id, &sender)?;13631364            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);13651366            // Modify permissions check1367            let target_collection = <Collection<T>>::get(collection_id);1368            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1369                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1370                Error::<T>::NoPermission);13711372            match target_collection.mode1373            {1374                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1375                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1376                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1377                _ => fail!(Error::<T>::UnexpectedCollectionType)1378            };13791380            Ok(())1381        }1382 1383        /// Set schema standard1384        /// ImageURL1385        /// Unique1386        /// 1387        /// # Permissions1388        /// 1389        /// * Collection Owner1390        /// * Collection Admin1391        /// 1392        /// # Arguments1393        /// 1394        /// * collection_id.1395        /// 1396        /// * schema: SchemaVersion: enum1397        #[weight = <T as Config>::WeightInfo::set_schema_version()]1398        #[transactional]1399        pub fn set_schema_version(1400            origin,1401            collection_id: CollectionId,1402            version: SchemaVersion1403        ) -> DispatchResult {1404            let sender = ensure_signed(origin)?;1405            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1406            let mut target_collection = <Collection<T>>::get(collection_id);1407            target_collection.schema_version = version;1408            <Collection<T>>::insert(collection_id, target_collection);14091410            Ok(())1411        }14121413        /// Set off-chain data schema.1414        /// 1415        /// # Permissions1416        /// 1417        /// * Collection Owner1418        /// * Collection Admin1419        /// 1420        /// # Arguments1421        /// 1422        /// * collection_id.1423        /// 1424        /// * schema: String representing the offchain data schema.1425        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1426        #[transactional]1427        pub fn set_offchain_schema(1428            origin,1429            collection_id: CollectionId,1430            schema: Vec<u8>1431        ) -> DispatchResult {1432            let sender = ensure_signed(origin)?;1433            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14341435            // check schema limit1436            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14371438            let mut target_collection = <Collection<T>>::get(collection_id);1439            target_collection.offchain_schema = schema;1440            <Collection<T>>::insert(collection_id, target_collection);14411442            Ok(())1443        }14441445        /// Set const on-chain data schema.1446        /// 1447        /// # Permissions1448        /// 1449        /// * Collection Owner1450        /// * Collection Admin1451        /// 1452        /// # Arguments1453        /// 1454        /// * collection_id.1455        /// 1456        /// * schema: String representing the const on-chain data schema.1457        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1458        #[transactional]1459        pub fn set_const_on_chain_schema (1460            origin,1461            collection_id: CollectionId,1462            schema: Vec<u8>1463        ) -> DispatchResult {1464            let sender = ensure_signed(origin)?;1465            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14661467            // check schema limit1468            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14691470            let mut target_collection = <Collection<T>>::get(collection_id);1471            target_collection.const_on_chain_schema = schema;1472            <Collection<T>>::insert(collection_id, target_collection);14731474            Ok(())1475        }14761477        /// Set variable on-chain data schema.1478        /// 1479        /// # Permissions1480        /// 1481        /// * Collection Owner1482        /// * Collection Admin1483        /// 1484        /// # Arguments1485        /// 1486        /// * collection_id.1487        /// 1488        /// * schema: String representing the variable on-chain data schema.1489        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1490        #[transactional]1491        pub fn set_variable_on_chain_schema (1492            origin,1493            collection_id: CollectionId,1494            schema: Vec<u8>1495        ) -> DispatchResult {1496            let sender = ensure_signed(origin)?;1497            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14981499            // check schema limit1500            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");15011502            let mut target_collection = <Collection<T>>::get(collection_id);1503            target_collection.variable_on_chain_schema = schema;1504            <Collection<T>>::insert(collection_id, target_collection);15051506            Ok(())1507        }15081509        // Sudo permissions function1510        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1511        #[transactional]1512        pub fn set_chain_limits(1513            origin,1514            limits: ChainLimits1515        ) -> DispatchResult {15161517            #[cfg(not(feature = "runtime-benchmarks"))]1518            ensure_root(origin)?;15191520            <ChainLimit>::put(limits);1521            Ok(())1522        }15231524        /// Enable smart contract self-sponsoring.1525        /// 1526        /// # Permissions1527        /// 1528        /// * Contract Owner1529        /// 1530        /// # Arguments1531        /// 1532        /// * contract address1533        /// * enable flag1534        /// 1535        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1536        #[transactional]1537        pub fn enable_contract_sponsoring(1538            origin,1539            contract_address: T::AccountId,1540            enable: bool1541        ) -> DispatchResult {15421543            let sender = ensure_signed(origin)?;15441545            #[cfg(feature = "runtime-benchmarks")]1546            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15471548            Self::ensure_contract_owned(sender, &contract_address)?;15491550            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1551            Ok(())1552        }15531554        /// Set the rate limit for contract sponsoring to specified number of blocks.1555        /// 1556        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1557        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1558        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1559        /// from contract endowment if there are at least B blocks between such transactions. 1560        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1561        /// 1562        /// # Permissions1563        /// 1564        /// * Contract Owner1565        /// 1566        /// # Arguments1567        /// 1568        /// -`contract_address`: Address of the contract to sponsor1569        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1570        /// 1571        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1572        #[transactional]1573        pub fn set_contract_sponsoring_rate_limit(1574            origin,1575            contract_address: T::AccountId,1576            rate_limit: T::BlockNumber1577        ) -> DispatchResult {1578            let sender = ensure_signed(origin)?;15791580            #[cfg(feature = "runtime-benchmarks")]1581            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15821583            Self::ensure_contract_owned(sender, &contract_address)?;1584            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1585            Ok(())1586        }15871588        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1589        /// 1590        /// # Permissions1591        /// 1592        /// * Address that deployed smart contract.1593        /// 1594        /// # Arguments1595        /// 1596        /// -`contract_address`: Address of the contract.1597        /// 1598        /// - `enable`: .  1599        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1600        #[transactional]1601        pub fn toggle_contract_white_list(1602            origin,1603            contract_address: T::AccountId,1604            enable: bool1605        ) -> DispatchResult {1606            let sender = ensure_signed(origin)?;16071608            #[cfg(feature = "runtime-benchmarks")]1609            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16101611            Self::ensure_contract_owned(sender, &contract_address)?;1612            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1613            Ok(())1614        }1615        1616        /// Add an address to smart contract white list.1617        /// 1618        /// # Permissions1619        /// 1620        /// * Address that deployed smart contract.1621        /// 1622        /// # Arguments1623        /// 1624        /// -`contract_address`: Address of the contract.1625        ///1626        /// -`account_address`: Address to add.1627        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1628        #[transactional]1629        pub fn add_to_contract_white_list(1630            origin,1631            contract_address: T::AccountId,1632            account_address: T::AccountId1633        ) -> DispatchResult {1634            let sender = ensure_signed(origin)?;16351636            #[cfg(feature = "runtime-benchmarks")]1637            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1638            1639            Self::ensure_contract_owned(sender, &contract_address)?;      1640            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1641            Ok(())1642        }16431644        /// Remove an address from smart contract white list.1645        /// 1646        /// # Permissions1647        /// 1648        /// * Address that deployed smart contract.1649        /// 1650        /// # Arguments1651        /// 1652        /// -`contract_address`: Address of the contract.1653        ///1654        /// -`account_address`: Address to remove.1655        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1656        #[transactional]1657        pub fn remove_from_contract_white_list(1658            origin,1659            contract_address: T::AccountId,1660            account_address: T::AccountId1661        ) -> DispatchResult {1662            let sender = ensure_signed(origin)?;16631664            #[cfg(feature = "runtime-benchmarks")]1665            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16661667            Self::ensure_contract_owned(sender, &contract_address)?;1668            <ContractWhiteList<T>>::remove(contract_address, account_address);1669            Ok(())1670        }16711672        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1673        #[transactional]1674        pub fn set_collection_limits(1675            origin,1676            collection_id: u32,1677            new_limits: CollectionLimits,1678        ) -> DispatchResult {1679            let sender = ensure_signed(origin)?;1680            Self::check_owner_permissions(collection_id, sender.clone())?;1681            let mut target_collection = <Collection<T>>::get(collection_id);1682            let old_limits = target_collection.limits;1683            let chain_limits = ChainLimit::get();16841685            // collection bounds1686            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1687                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1688                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1689                Error::<T>::CollectionLimitBoundsExceeded);16901691            // token_limit   check  prev1692            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1693            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16941695            ensure!(1696                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1697                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1698                Error::<T>::OwnerPermissionsCantBeReverted,1699            );17001701            target_collection.limits = new_limits;1702            <Collection<T>>::insert(collection_id, target_collection);17031704            Ok(())1705        } 1706    }1707}17081709impl<T: Config> Module<T> {17101711    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {17121713        let target_collection = <Collection<T>>::get(collection_id);17141715        // Limits check1716        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;17171718        // Transfer permissions check1719        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1720            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1721            Error::<T>::NoPermission);17221723        if target_collection.access == AccessMode::WhiteList {1724            Self::check_white_list(collection_id, &sender)?;1725            Self::check_white_list(collection_id, &recipient)?;1726        }17271728        match target_collection.mode1729        {1730            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1731            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1732            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1733            _ => ()1734        };17351736        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));17371738        Ok(())1739    }174017411742    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, recipient: &T::AccountId) -> DispatchResult {17431744        // check token limit and account token limit1745        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1746        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1747        1748        Ok(())1749    }17501751    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T::AccountId>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {17521753        // check token limit and account token limit1754        let total_items: u32 = ItemListIndex::get(collection_id);1755        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1756        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1757        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);17581759        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1760            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1761            Self::check_white_list(collection_id, owner)?;1762            Self::check_white_list(collection_id, sender)?;1763        }17641765        Ok(())1766    }17671768    fn validate_create_item_args(target_collection: &CollectionType<T::AccountId>, data: &CreateItemData) -> DispatchResult {1769        match target_collection.mode1770        {1771            CollectionMode::NFT => {1772                if let CreateItemData::NFT(data) = data {1773                    // check sizes1774                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1775                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1776                } else {1777                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1778                }1779            },1780            CollectionMode::Fungible(_) => {1781                if let CreateItemData::Fungible(_) = data {1782                } else {1783                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1784                }1785            },1786            CollectionMode::ReFungible => {1787                if let CreateItemData::ReFungible(data) = data {17881789                    // check sizes1790                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1791                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);17921793                    // Check refungibility limits1794                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1795                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1796                } else {1797                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1798                }1799            },1800            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1801        };18021803        Ok(())1804    }18051806    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1807        match data1808        {1809            CreateItemData::NFT(data) => {1810                let item = NftItemType {1811                    owner: owner.clone(),1812                    const_data: data.const_data,1813                    variable_data: data.variable_data1814                };18151816                Self::add_nft_item(collection_id, item)?;1817            },1818            CreateItemData::Fungible(data) => {1819                Self::add_fungible_item(collection_id, &owner, data.value)?;1820            },1821            CreateItemData::ReFungible(data) => {1822                let mut owner_list = Vec::new();1823                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});18241825                let item = ReFungibleItemType {1826                    owner: owner_list,1827                    const_data: data.const_data,1828                    variable_data: data.variable_data1829                };18301831                Self::add_refungible_item(collection_id, item)?;1832            }1833        };18341835        Ok(())1836    }18371838    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {18391840        // Does new owner already have an account?1841        let mut balance: u128 = 0;1842        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1843            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1844        } 18451846        // Mint 1847        let item = FungibleItemType {1848            value: balance + value1849        };1850        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);18511852        // Update balance1853        let new_balance = <Balance<T>>::get(collection_id, owner)1854            .checked_add(value)1855            .ok_or(Error::<T>::NumOverflow)?;1856        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18571858        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));1859        Ok(())1860    }18611862    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1863        let current_index = <ItemListIndex>::get(collection_id)1864            .checked_add(1)1865            .ok_or(Error::<T>::NumOverflow)?;1866        let itemcopy = item.clone();18671868        ensure!(1869            item.owner.len() == 1,1870            Error::<T>::BadCreateRefungibleCall,1871        );1872        let item_owner = item.owner.first().expect("only one owner is defined");18731874        let value = item_owner.fraction;1875        let owner = item_owner.owner.clone();18761877        Self::add_token_index(collection_id, current_index, &owner)?;18781879        <ItemListIndex>::insert(collection_id, current_index);1880        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);18811882        // Update balance1883        let new_balance = <Balance<T>>::get(collection_id, &owner)1884            .checked_add(value)1885            .ok_or(Error::<T>::NumOverflow)?;1886        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);18871888        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));1889        Ok(())1890    }18911892    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1893        let current_index = <ItemListIndex>::get(collection_id)1894            .checked_add(1)1895            .ok_or(Error::<T>::NumOverflow)?;18961897        let item_owner = item.owner.clone();1898        Self::add_token_index(collection_id, current_index, &item.owner)?;18991900        <ItemListIndex>::insert(collection_id, current_index);1901        <NftItemList<T>>::insert(collection_id, current_index, item);19021903        // Update balance1904        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1905            .checked_add(1)1906            .ok_or(Error::<T>::NumOverflow)?;1907        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);19081909        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));1910        Ok(())1911    }19121913    fn burn_refungible_item(1914        collection_id: CollectionId,1915        item_id: TokenId,1916        owner: &T::AccountId,1917    ) -> DispatchResult {1918        ensure!(1919            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1920            Error::<T>::TokenNotFound1921        );1922        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1923        let rft_balance = token1924            .owner1925            .iter()1926            .find(|&i| i.owner == *owner)1927            .ok_or(Error::<T>::TokenNotFound)?;1928        Self::remove_token_index(collection_id, item_id, owner)?;19291930        // update balance1931        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1932            .checked_sub(rft_balance.fraction)1933            .ok_or(Error::<T>::NumOverflow)?;1934        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);19351936        // Re-create owners list with sender removed1937        let index = token1938            .owner1939            .iter()1940            .position(|i| i.owner == *owner)1941            .expect("owned item is exists");1942        token.owner.remove(index);1943        let owner_count = token.owner.len();19441945        // Burn the token completely if this was the last (only) owner1946        if owner_count == 0 {1947            <ReFungibleItemList<T>>::remove(collection_id, item_id);1948        }1949        else {1950            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1951        }19521953        Ok(())1954    }19551956    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1957        ensure!(1958            <NftItemList<T>>::contains_key(collection_id, item_id),1959            Error::<T>::TokenNotFound1960        );1961        let item = <NftItemList<T>>::get(collection_id, item_id);1962        Self::remove_token_index(collection_id, item_id, &item.owner)?;19631964        // update balance1965        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1966            .checked_sub(1)1967            .ok_or(Error::<T>::NumOverflow)?;1968        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1969        <NftItemList<T>>::remove(collection_id, item_id);19701971        Ok(())1972    }19731974    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1975        ensure!(1976            <FungibleItemList<T>>::contains_key(collection_id, owner),1977            Error::<T>::TokenNotFound1978        );1979        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1980        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);19811982        // update balance1983        let new_balance = <Balance<T>>::get(collection_id, owner)1984            .checked_sub(value)1985            .ok_or(Error::<T>::NumOverflow)?;1986        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);19871988        if balance.value - value > 0 {1989            balance.value -= value;1990            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1991        }1992        else {1993            <FungibleItemList<T>>::remove(collection_id, owner);1994        }19951996        Ok(())1997    }19981999    fn collection_exists(collection_id: CollectionId) -> DispatchResult {2000        ensure!(2001            <Collection<T>>::contains_key(collection_id),2002            Error::<T>::CollectionNotFound2003        );2004        Ok(())2005    }20062007    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {2008        Self::collection_exists(collection_id)?;20092010        let target_collection = <Collection<T>>::get(collection_id);2011        ensure!(2012            subject == target_collection.owner,2013            Error::<T>::NoPermission2014        );20152016        Ok(())2017    }20182019    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {2020        let target_collection = <Collection<T>>::get(collection_id);2021        let mut result: bool = subject == target_collection.owner;2022        let exists = <AdminList<T>>::contains_key(collection_id);20232024        if !result & exists {2025            if <AdminList<T>>::get(collection_id).contains(&subject) {2026                result = true2027            }2028        }20292030        result2031    }20322033    fn check_owner_or_admin_permissions(2034        collection_id: CollectionId,2035        subject: T::AccountId,2036    ) -> DispatchResult {2037        Self::collection_exists(collection_id)?;2038        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());20392040        ensure!(2041            result,2042            Error::<T>::NoPermission2043        );2044        Ok(())2045    }20462047    fn owned_amount(2048        subject: T::AccountId,2049        collection_id: CollectionId,2050        item_id: TokenId,2051    ) -> Option<u128> {2052        let target_collection = <Collection<T>>::get(collection_id);20532054        match target_collection.mode {2055            CollectionMode::NFT => {2056                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {2057                    return Some(1)2058                }2059                None2060            },2061            CollectionMode::Fungible(_) => {2062                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {2063                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)2064                        .value);2065                }2066                None2067            },2068            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)2069                .owner2070                .iter()2071                .find(|i| i.owner == subject)2072                .map(|i| i.fraction),2073            CollectionMode::Invalid => None,2074        }2075    }20762077    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {2078        let target_collection = <Collection<T>>::get(collection_id);20792080        match target_collection.mode {2081            CollectionMode::NFT => {2082                <NftItemList<T>>::get(collection_id, item_id).owner == subject2083            }2084            CollectionMode::Fungible(_) => {2085                <FungibleItemList<T>>::contains_key(collection_id, &subject)2086            }2087            CollectionMode::ReFungible => {2088                <ReFungibleItemList<T>>::get(collection_id, item_id)2089                    .owner2090                    .iter()2091                    .any(|i| i.owner == subject)2092            }2093            CollectionMode::Invalid => false,2094        }2095    }20962097    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {2098        let mes = Error::<T>::AddresNotInWhiteList;2099        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);21002101        Ok(())2102    }21032104    /// Check if token exists. In case of Fungible, check if there is an entry for 2105    /// the owner in fungible balances double map2106    fn token_exists(2107        collection_id: CollectionId,2108        item_id: TokenId,2109        owner: &T::AccountId2110    ) -> DispatchResult {2111        let target_collection = <Collection<T>>::get(collection_id);2112        let exists = match target_collection.mode2113        {2114            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2115            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2116            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2117            _ => false2118        };21192120        ensure!(exists == true, Error::<T>::TokenNotFound);2121        Ok(())2122    }21232124    fn transfer_fungible(2125        collection_id: CollectionId,2126        value: u128,2127        owner: &T::AccountId,2128        recipient: &T::AccountId,2129    ) -> DispatchResult {2130        Self::token_exists(collection_id, 0, owner)?;21312132        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2133        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);21342135        // Send balance to recipient (updates balanceOf of recipient)2136        Self::add_fungible_item(collection_id, recipient, value)?;21372138        // update balanceOf of sender2139        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);21402141        // Reduce or remove sender2142        if balance.value == value {2143            <FungibleItemList<T>>::remove(collection_id, owner);2144        }2145        else {2146            balance.value -= value;2147            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2148        }21492150        Ok(())2151    }21522153    fn transfer_refungible(2154        collection_id: CollectionId,2155        item_id: TokenId,2156        value: u128,2157        owner: T::AccountId,2158        new_owner: T::AccountId,2159    ) -> DispatchResult {2160        Self::token_exists(collection_id, item_id, &owner)?;21612162        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2163        let item = full_item2164            .owner2165            .iter()2166            .filter(|i| i.owner == owner)2167            .next()2168            .ok_or(Error::<T>::TokenNotFound)?;2169        let amount = item.fraction;21702171        ensure!(amount >= value, Error::<T>::TokenValueTooLow);21722173        // update balance2174        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2175            .checked_sub(value)2176            .ok_or(Error::<T>::NumOverflow)?;2177        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21782179        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2180            .checked_add(value)2181            .ok_or(Error::<T>::NumOverflow)?;2182        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21832184        let old_owner = item.owner.clone();2185        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);21862187        // transfer2188        if amount == value && !new_owner_has_account {2189            // change owner2190            // new owner do not have account2191            let mut new_full_item = full_item.clone();2192            new_full_item2193                .owner2194                .iter_mut()2195                .find(|i| i.owner == owner)2196                .expect("old owner does present in refungible")2197                .owner = new_owner.clone();2198            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21992200            // update index collection2201            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2202        } else {2203            let mut new_full_item = full_item.clone();2204            new_full_item2205                .owner2206                .iter_mut()2207                .find(|i| i.owner == owner)2208                .expect("old owner does present in refungible")2209                .fraction -= value;22102211            // separate amount2212            if new_owner_has_account {2213                // new owner has account2214                new_full_item2215                    .owner2216                    .iter_mut()2217                    .find(|i| i.owner == new_owner)2218                    .expect("new owner has account")2219                    .fraction += value;2220            } else {2221                // new owner do not have account2222                new_full_item.owner.push(Ownership {2223                    owner: new_owner.clone(),2224                    fraction: value,2225                });2226                Self::add_token_index(collection_id, item_id, &new_owner)?;2227            }22282229            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2230        }22312232        Ok(())2233    }22342235    fn transfer_nft(2236        collection_id: CollectionId,2237        item_id: TokenId,2238        sender: T::AccountId,2239        new_owner: T::AccountId,2240    ) -> DispatchResult {2241        Self::token_exists(collection_id, item_id, &sender)?;22422243        let mut item = <NftItemList<T>>::get(collection_id, item_id);22442245        ensure!(2246            sender == item.owner,2247            Error::<T>::MustBeTokenOwner2248        );22492250        // update balance2251        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2252            .checked_sub(1)2253            .ok_or(Error::<T>::NumOverflow)?;2254        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);22552256        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2257            .checked_add(1)2258            .ok_or(Error::<T>::NumOverflow)?;2259        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);22602261        // change owner2262        let old_owner = item.owner.clone();2263        item.owner = new_owner.clone();2264        <NftItemList<T>>::insert(collection_id, item_id, item);22652266        // update index collection2267        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;22682269        Ok(())2270    }2271    2272    fn set_re_fungible_variable_data(2273        collection_id: CollectionId,2274        item_id: TokenId,2275        data: Vec<u8>2276    ) -> DispatchResult {2277        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);22782279        item.variable_data = data;22802281        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);22822283        Ok(())2284    }22852286    fn set_nft_variable_data(2287        collection_id: CollectionId,2288        item_id: TokenId,2289        data: Vec<u8>2290    ) -> DispatchResult {2291        let mut item = <NftItemList<T>>::get(collection_id, item_id);2292        2293        item.variable_data = data;22942295        <NftItemList<T>>::insert(collection_id, item_id, item);2296        2297        Ok(())2298    }22992300    fn init_collection(item: &CollectionType<T::AccountId>) {2301        // check params2302        assert!(2303            item.decimal_points <= MAX_DECIMAL_POINTS,2304            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2305        );2306        assert!(2307            item.name.len() <= 64,2308            "Collection name can not be longer than 63 char"2309        );2310        assert!(2311            item.name.len() <= 256,2312            "Collection description can not be longer than 255 char"2313        );2314        assert!(2315            item.token_prefix.len() <= 16,2316            "Token prefix can not be longer than 15 char"2317        );23182319        // Generate next collection ID2320        let next_id = CreatedCollectionCount::get()2321            .checked_add(1)2322            .unwrap();23232324        CreatedCollectionCount::put(next_id);2325    }23262327    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2328        let current_index = <ItemListIndex>::get(collection_id)2329            .checked_add(1)2330            .unwrap();23312332        let item_owner = item.owner.clone();2333        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();23342335        <ItemListIndex>::insert(collection_id, current_index);23362337        // Update balance2338        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2339            .checked_add(1)2340            .unwrap();2341        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2342    }23432344    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2345        let current_index = <ItemListIndex>::get(collection_id)2346            .checked_add(1)2347            .unwrap();23482349        Self::add_token_index(collection_id, current_index, owner).unwrap();23502351        <ItemListIndex>::insert(collection_id, current_index);23522353        // Update balance2354        let new_balance = <Balance<T>>::get(collection_id, owner)2355            .checked_add(item.value)2356            .unwrap();2357        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2358    }23592360    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2361        let current_index = <ItemListIndex>::get(collection_id)2362            .checked_add(1)2363            .unwrap();23642365        let value = item.owner.first().unwrap().fraction;2366        let owner = item.owner.first().unwrap().owner.clone();23672368        Self::add_token_index(collection_id, current_index, &owner).unwrap();23692370        <ItemListIndex>::insert(collection_id, current_index);23712372        // Update balance2373        let new_balance = <Balance<T>>::get(collection_id, &owner)2374            .checked_add(value)2375            .unwrap();2376        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2377    }23782379    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {23802381        // add to account limit2382        if <AccountItemCount<T>>::contains_key(owner) {23832384            // bound Owned tokens by a single address2385            let count = <AccountItemCount<T>>::get(owner);2386            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);23872388            <AccountItemCount<T>>::insert(owner.clone(), count2389                .checked_add(1)2390                .ok_or(Error::<T>::NumOverflow)?);2391        }2392        else {2393            <AccountItemCount<T>>::insert(owner.clone(), 1);2394        }23952396        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2397        if list_exists {2398            let mut list = <AddressTokens<T>>::get(collection_id, owner);2399            let item_contains = list.contains(&item_index.clone());24002401            if !item_contains {2402                list.push(item_index.clone());2403            }24042405            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2406        } else {2407            let mut itm = Vec::new();2408            itm.push(item_index.clone());2409            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2410        }24112412        Ok(())2413    }24142415    fn remove_token_index(2416        collection_id: CollectionId,2417        item_index: TokenId,2418        owner: &T::AccountId,2419    ) -> DispatchResult {24202421        // update counter2422        <AccountItemCount<T>>::insert(owner.clone(), 2423            <AccountItemCount<T>>::get(owner)2424            .checked_sub(1)2425            .ok_or(Error::<T>::NumOverflow)?);242624272428        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2429        if list_exists {2430            let mut list = <AddressTokens<T>>::get(collection_id, owner);2431            let item_contains = list.contains(&item_index.clone());24322433            if item_contains {2434                list.retain(|&item| item != item_index);2435                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2436            }2437        }24382439        Ok(())2440    }24412442    fn move_token_index(2443        collection_id: CollectionId,2444        item_index: TokenId,2445        old_owner: &T::AccountId,2446        new_owner: &T::AccountId,2447    ) -> DispatchResult {2448        Self::remove_token_index(collection_id, item_index, old_owner)?;2449        Self::add_token_index(collection_id, item_index, new_owner)?;24502451        Ok(())2452    }2453    2454    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2455        if <ContractOwner<T>>::contains_key(contract.clone()) {2456            let owner = <ContractOwner<T>>::get(contract);2457            ensure!(account == owner, Error::<T>::NoPermission);2458        } else {2459            fail!(Error::<T>::NoPermission);2460        }24612462        Ok(())2463    }2464}24652466////////////////////////////////////////////////////////////////////////////////////////////////////2467// Economic models2468// #region24692470/// Fee multiplier.2471pub type Multiplier = FixedU128;24722473type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;24742475/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2476/// in the queue.2477#[derive(Encode, Decode, Clone, Eq, PartialEq)]2478pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);24792480impl<T: Config + Send + Sync> sp_std::fmt::Debug 2481    for ChargeTransactionPayment<T>2482{2483	#[cfg(feature = "std")]2484	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2485		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2486	}2487	#[cfg(not(feature = "std"))]2488	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2489		Ok(())2490	}2491}24922493impl<T: Config> ChargeTransactionPayment<T>2494where2495    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2496    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2497    T::AccountId: AsRef<[u8]>,2498    T::AccountId: UncheckedFrom<T::Hash>,2499{2500    fn traditional_fee(2501        len: usize,2502        info: &DispatchInfoOf<T::Call>,2503        tip: BalanceOf<T>,2504    ) -> BalanceOf<T>2505    where2506        T::Call: Dispatchable<Info = DispatchInfo>,2507    {2508        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2509    }25102511	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2512        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2513        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2514        let len_saturation = max_block_length as u64 / (len as u64).max(1);2515        let coefficient: BalanceOf<T> = weight_saturation2516            .min(len_saturation)2517            .saturated_into::<BalanceOf<T>>();2518        final_fee2519            .saturating_mul(coefficient)2520            .saturated_into::<TransactionPriority>()2521    }25222523    fn withdraw_fee(2524        &self,2525        who: &T::AccountId,2526        call: &T::Call,2527        info: &DispatchInfoOf<T::Call>,2528        len: usize,2529	) -> Result<2530		(2531			BalanceOf<T>,2532			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2533		),2534		TransactionValidityError,2535	> {2536        let tip = self.0;25372538        // Set fee based on call type. Creating collection costs 1 Unique.2539        // All other transactions have traditional fees so far2540        // let fee = match call.is_sub_type() {2541        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2542        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2543        //                                                 // _ => <BalanceOf<T>>::from(100)2544        // };2545        let fee = Self::traditional_fee(len, info, tip);25462547        // Only mess with balances if fee is not zero.2548        if fee.is_zero() {2549            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2550			.map(|i| (fee, i));2551        }25522553        // Determine who is paying transaction fee based on ecnomic model2554        // Parse call to extract collection ID and access collection sponsor2555        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2556            Some(Call::create_item(collection_id, _owner, _properties)) => {25572558                // sponsor timeout2559                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25602561                let collection = <Collection<T>>::get(collection_id);25622563                let limit = collection.limits.sponsor_transfer_timeout;2564                let mut sponsored = true;2565                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2566                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2567                    let limit_time = last_tx_block + limit.into();2568                    if block_number <= limit_time {2569                        sponsored = false;2570                    }2571                }2572                if sponsored {2573                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2574                }25752576                // check free create limit2577                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2578                   (sponsored)2579                {2580                    collection.sponsorship.sponsor()2581                        .cloned()2582                        .unwrap_or_default()2583                } else {2584                    T::AccountId::default()2585                }2586            }2587            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2588                2589                let mut sponsor_transfer = false;2590                if <Collection<T>>::get(collection_id).sponsorship.confirmed() {25912592                    let collection_limits = <Collection<T>>::get(collection_id).limits;2593                    let collection_mode = <Collection<T>>::get(collection_id).mode;2594    2595                    // sponsor timeout2596                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2597                    sponsor_transfer = match collection_mode {2598                        CollectionMode::NFT => {2599    2600                            // get correct limit2601                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2602                                collection_limits.sponsor_transfer_timeout2603                            } else {2604                                ChainLimit::get().nft_sponsor_transfer_timeout2605                            };2606    2607                            let mut sponsored = true;2608                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2609                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2610                                let limit_time = last_tx_block + limit.into();2611                                if block_number <= limit_time {2612                                    sponsored = false;2613                                }2614                            }2615                            if sponsored {2616                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2617                            }26182619                            sponsored2620                        }2621                        CollectionMode::Fungible(_) => {2622    2623                            // get correct limit2624                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2625                                collection_limits.sponsor_transfer_timeout2626                            } else {2627                                ChainLimit::get().fungible_sponsor_transfer_timeout2628                            };2629    2630                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2631                            let mut sponsored = true;2632                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2633                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2634                                let limit_time = last_tx_block + limit.into();2635                                if block_number <= limit_time {2636                                    sponsored = false;2637                                }2638                            }2639                            if sponsored {2640                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2641                            }26422643                            sponsored2644                        }2645                        CollectionMode::ReFungible => {2646    2647                            // get correct limit2648                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2649                                collection_limits.sponsor_transfer_timeout2650                            } else {2651                                ChainLimit::get().refungible_sponsor_transfer_timeout2652                            };2653    2654                            let mut sponsored = true;2655                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2656                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2657                                let limit_time = last_tx_block + limit.into();2658                                if block_number <= limit_time {2659                                    sponsored = false;2660                                }2661                            }2662                            if sponsored {2663                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2664                            }26652666                            sponsored2667                        }2668                        _ => {2669                            false2670                        },2671                    };2672                }26732674                if !sponsor_transfer {2675                    T::AccountId::default()2676                } else {2677                    <Collection<T>>::get(collection_id).sponsorship.sponsor()2678                        .cloned()2679                        .unwrap_or_default()2680                }2681            }26822683            _ => T::AccountId::default(),2684        };26852686        // Sponsor smart contracts2687        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26882689            // On instantiation: set the contract owner2690            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26912692                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2693                    &who,2694                    code_hash,2695                    salt,2696                );2697                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26982699                T::AccountId::default()2700            },27012702            // On instantiation with code: set the contract owner2703            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {27042705                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2706                    &who,2707                    &T::Hashing::hash(&_code),2708                    _salt,2709                );27102711                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());27122713                T::AccountId::default()2714            }27152716            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2717            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {27182719                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());27202721                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2722                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2723                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2724                  2725                if !owned_contract && white_list_enabled {2726                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2727                        return Err(InvalidTransaction::Call.into());2728                    }2729                }27302731                let mut sponsor_transfer = false;2732                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2733                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2734                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2735                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2736                    let limit_time = last_tx_block + rate_limit;27372738                    if block_number >= limit_time {2739                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2740                        sponsor_transfer = true;2741                    }2742                } else {2743                    sponsor_transfer = false;2744                }2745               2746                2747                let mut sp = T::AccountId::default();2748                if sponsor_transfer {2749                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2750                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2751                            sp = called_contract;2752                        }2753                    }2754                }27552756                sp2757            },27582759            _ => sponsor,2760        };27612762        let mut who_pays_fee: T::AccountId = sponsor.clone();2763        if sponsor == T::AccountId::default() {2764            who_pays_fee = who.clone();2765        }27662767		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2768			.map(|i| (fee, i))2769    }2770}277127722773impl<T: Config + Send + Sync> SignedExtension2774    for ChargeTransactionPayment<T>2775where2776    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2777    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2778    T::AccountId: AsRef<[u8]>,2779    T::AccountId: UncheckedFrom<T::Hash>,2780{2781    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2782    type AccountId = T::AccountId;2783    type Call = T::Call;2784    type AdditionalSigned = ();2785    type Pre = (2786        // tip2787        BalanceOf<T>,2788        // who pays fee2789        Self::AccountId,2790		// imbalance resulting from withdrawing the fee2791		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2792    );2793    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2794        Ok(())2795    }27962797    fn validate(2798        &self,2799        who: &Self::AccountId,2800        call: &Self::Call,2801        info: &DispatchInfoOf<Self::Call>,2802        len: usize,2803    ) -> TransactionValidity {2804		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2805		Ok(ValidTransaction {2806			priority: Self::get_priority(len, info, fee),2807			..Default::default()2808		})2809    }28102811    fn pre_dispatch(2812        self,2813        who: &Self::AccountId,2814        call: &Self::Call,2815        info: &DispatchInfoOf<Self::Call>,2816        len: usize,2817    ) -> Result<Self::Pre, TransactionValidityError> {2818        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2819        Ok((self.0, who.clone(), imbalance))2820    }28212822    fn post_dispatch(2823        pre: Self::Pre,2824        info: &DispatchInfoOf<Self::Call>,2825        post_info: &PostDispatchInfoOf<Self::Call>,2826        len: usize,2827        _result: &DispatchResult,2828    ) -> Result<(), TransactionValidityError> {2829		let (tip, who, imbalance) = pre;2830		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2831			len as u32,2832			info,2833			post_info,2834			tip,2835		);2836		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2837		Ok(())2838    }2839}28402841// #endregion