git.delta.rocks / unique-network / refs/commits / 572dabd231f6

difftreelog

source

pallets/nft/src/lib.rs104.0 KiBsourcehistory
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