git.delta.rocks / unique-network / refs/commits / 280307ce9910

difftreelog

source

pallets/nft/src/lib.rs101.4 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};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct CollectionType<T: Config> {129    pub owner: T::AccountId,130    pub mode: CollectionMode,131    pub access: AccessMode,132    pub decimal_points: DecimalPoints,133    pub name: Vec<u16>,        // 64 include null escape char134    pub description: Vec<u16>, // 256 include null escape char135    pub token_prefix: Vec<u8>, // 16 include null escape char136    pub mint_mode: bool,137    pub offchain_schema: Vec<u8>,138    pub schema_version: SchemaVersion,139    pub sponsor: T::AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender140    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.141    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 142    pub variable_on_chain_schema: Vec<u8>, //143    pub const_on_chain_schema: Vec<u8>, //144}145146#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]147#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]148pub struct NftItemType<AccountId> {149    pub owner: AccountId,150    pub const_data: Vec<u8>,151    pub variable_data: Vec<u8>,152}153154#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]155#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]156pub struct FungibleItemType {157    pub value: u128,158}159160#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]161#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]162pub struct ReFungibleItemType<AccountId> {163    pub owner: Vec<Ownership<AccountId>>,164    pub const_data: Vec<u8>,165    pub variable_data: Vec<u8>,166}167168// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]169// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170// pub struct VestingItem<AccountId, Moment> {171//     pub sender: AccountId,172//     pub recipient: AccountId,173//     pub collection_id: CollectionId,174//     pub item_id: TokenId,175//     pub amount: u64,176//     pub vesting_date: Moment,177// }178179#[derive(Encode, Decode, Debug, Clone, PartialEq)]180#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]181pub struct CollectionLimits<BlockNumber: Encode + Decode> {182    pub account_token_ownership_limit: u32,183    pub sponsored_data_size: u32,184    /// None - setVariableMetadata is not sponsored185    /// Some(v) - setVariableMetadata is sponsored 186    ///           if there is v block between txs187    pub sponsored_data_rate_limit: Option<BlockNumber>,188    pub token_limit: u32,189190    // Timeouts for item types in passed blocks191    pub sponsor_transfer_timeout: u32,192    pub owner_can_transfer: bool,193    pub owner_can_destroy: bool,194}195196impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {197    fn default() -> Self {198        Self { 199            account_token_ownership_limit: 10_000_000, 200            token_limit: u32::max_value(),201            sponsored_data_size: u32::MAX, 202            sponsored_data_rate_limit: None,203            sponsor_transfer_timeout: 14400,204            owner_can_transfer: true,205            owner_can_destroy: true206        }207    }208}209210#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]211#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]212pub struct ChainLimits {213    pub collection_numbers_limit: u32,214    pub account_token_ownership_limit: u32,215    pub collections_admins_limit: u64,216    pub custom_data_limit: u32,217218    // Timeouts for item types in passed blocks219    pub nft_sponsor_transfer_timeout: u32,220    pub fungible_sponsor_transfer_timeout: u32,221    pub refungible_sponsor_transfer_timeout: u32,222223    // Schema limits224    pub offchain_schema_limit: u32,225    pub variable_on_chain_schema_limit: u32,226    pub const_on_chain_schema_limit: u32,227}228229pub trait WeightInfo {230	fn create_collection() -> Weight;231	fn destroy_collection() -> Weight;232	fn add_to_white_list() -> Weight;233	fn remove_from_white_list() -> Weight;234    fn set_public_access_mode() -> Weight;235    fn set_mint_permission() -> Weight;236    fn change_collection_owner() -> Weight;237    fn add_collection_admin() -> Weight;238    fn remove_collection_admin() -> Weight;239    fn set_collection_sponsor() -> Weight;240    fn confirm_sponsorship() -> Weight;241    fn remove_collection_sponsor() -> Weight;242    fn create_item(s: usize) -> Weight;243    fn burn_item() -> Weight;244    fn transfer() -> Weight;245    fn approve() -> Weight;246    fn transfer_from() -> Weight;247    fn set_offchain_schema() -> Weight;248    fn set_const_on_chain_schema() -> Weight;249    fn set_variable_on_chain_schema() -> Weight;250    fn set_variable_meta_data() -> Weight;251    fn enable_contract_sponsoring() -> Weight;252    fn set_schema_version() -> Weight;253    fn set_chain_limits() -> Weight;254    fn set_contract_sponsoring_rate_limit() -> Weight;255    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;256    fn toggle_contract_white_list() -> Weight;257    fn add_to_contract_white_list() -> Weight;258    fn remove_from_contract_white_list() -> Weight;259    fn set_collection_limits() -> Weight;260}261262#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]263#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]264pub struct CreateNftData {265    pub const_data: Vec<u8>,266    pub variable_data: Vec<u8>,267}268269#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]270#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]271pub struct CreateFungibleData {272    pub value: u128,273}274275#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]276#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]277pub struct CreateReFungibleData {278    pub const_data: Vec<u8>,279    pub variable_data: Vec<u8>,280    pub pieces: u128,281}282283#[derive(Encode, Decode, Debug, Clone, PartialEq)]284#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]285pub enum CreateItemData {286    NFT(CreateNftData),287    Fungible(CreateFungibleData),288    ReFungible(CreateReFungibleData),289}290291impl CreateItemData {292    pub fn len(&self) -> usize {293        let len = match self {294            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),295            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),296            _ => 0297        };298        299        return len;300    }301}302303impl From<CreateNftData> for CreateItemData {304    fn from(item: CreateNftData) -> Self {305        CreateItemData::NFT(item)306    }307}308309impl From<CreateReFungibleData> for CreateItemData {310    fn from(item: CreateReFungibleData) -> Self {311        CreateItemData::ReFungible(item)312    }313}314315impl From<CreateFungibleData> for CreateItemData {316    fn from(item: CreateFungibleData) -> Self {317        CreateItemData::Fungible(item)318    }319}320321322decl_error! {323	/// Error for non-fungible-token module.324	pub enum Error for Module<T: Config> {325        /// Total collections bound exceeded.326        TotalCollectionsLimitExceeded,327		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.328        CollectionDecimalPointLimitExceeded, 329        /// Collection name can not be longer than 63 char.330        CollectionNameLimitExceeded, 331        /// Collection description can not be longer than 255 char.332        CollectionDescriptionLimitExceeded, 333        /// Token prefix can not be longer than 15 char.334        CollectionTokenPrefixLimitExceeded,335        /// This collection does not exist.336        CollectionNotFound,337        /// Item not exists.338        TokenNotFound,339        /// Admin not found340        AdminNotFound,341        /// Arithmetic calculation overflow.342        NumOverflow,       343        /// Account already has admin role.344        AlreadyAdmin,  345        /// You do not own this collection.346        NoPermission,347        /// This address is not set as sponsor, use setCollectionSponsor first.348        ConfirmUnsetSponsorFail,349        /// Collection is not in mint mode.350        PublicMintingNotAllowed,351        /// Sender parameter and item owner must be equal.352        MustBeTokenOwner,353        /// Item balance not enough.354        TokenValueTooLow,355        /// Size of item is too large.356        NftSizeLimitExceeded,357        /// No approve found358        ApproveNotFound,359        /// Requested value more than approved.360        TokenValueNotEnough,361        /// Only approved addresses can call this method.362        ApproveRequired,363        /// Address is not in white list.364        AddresNotInWhiteList,365        /// Number of collection admins bound exceeded.366        CollectionAdminsLimitExceeded,367        /// Owned tokens by a single address bound exceeded.368        AddressOwnershipLimitExceeded,369        /// Length of items properties must be greater than 0.370        EmptyArgument,371        /// const_data exceeded data limit.372        TokenConstDataLimitExceeded,373        /// variable_data exceeded data limit.374        TokenVariableDataLimitExceeded,375        /// Not NFT item data used to mint in NFT collection.376        NotNftDataUsedToMintNftCollectionToken,377        /// Not Fungible item data used to mint in Fungible collection.378        NotFungibleDataUsedToMintFungibleCollectionToken,379        /// Not Re Fungible item data used to mint in Re Fungible collection.380        NotReFungibleDataUsedToMintReFungibleCollectionToken,381        /// Unexpected collection type.382        UnexpectedCollectionType,383        /// Can't store metadata in fungible tokens.384        CantStoreMetadataInFungibleTokens,385        /// Collection token limit exceeded386        CollectionTokenLimitExceeded,387        /// Account token limit exceeded per collection388        AccountTokenLimitExceeded,389        /// Collection limit bounds per collection exceeded390        CollectionLimitBoundsExceeded,391        /// Tried to enable permissions which are only permitted to be disabled392        OwnerPermissionsCantBeReverted,393        /// Schema data size limit bound exceeded394        SchemaDataLimitExceeded,395        /// Maximum refungibility exceeded396        WrongRefungiblePieces397	}398}399400pub trait Config: system::Config + Sized + transaction_payment::Config + pallet_contracts::Config {401    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;402403    /// Weight information for extrinsics in this pallet.404	type WeightInfo: WeightInfo;405}406407#[cfg(feature = "runtime-benchmarks")]408mod benchmarking;409410// #endregion411412decl_storage! {413    trait Store for Module<T: Config> as Nft {414415        // Private members416        NextCollectionID: CollectionId;417        CreatedCollectionCount: u32;418        ChainVersion: u64;419        ItemListIndex: map hasher(identity) CollectionId => TokenId;420421        // Chain limits struct422        pub ChainLimit get(fn chain_limit) config(): ChainLimits;423424        // Bound counters425        CollectionCount: u32;426        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;427428        // Basic collections429        pub Collection get(fn collection) config(): map hasher(identity) CollectionId => Option<CollectionType<T>> = None;430        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;431        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;432433        /// Balance owner per collection map434        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;435436        /// second parameter: item id + owner account id + spender account id437        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;438439        /// Item collections440        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;441        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;442        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;443444        /// Index list445        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;446447        /// Tokens transfer baskets448        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;449        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;450        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;451        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;452453        /// Variable metadata sponsoring454        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;455456        // Contract Sponsorship and Ownership457        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;458        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;459        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;460        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;461        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 462        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 463    }464    add_extra_genesis {465        build(|config: &GenesisConfig<T>| {466            // Modification of storage467            for (_num, _c) in &config.collection {468                <Module<T>>::init_collection(_c);469            }470471            for (_num, _c, _i) in &config.nft_item_id {472                <Module<T>>::init_nft_token(*_c, _i);473            }474475            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {476                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);477            }478479            for (_num, _c, _i) in &config.refungible_item_id {480                <Module<T>>::init_refungible_token(*_c, _i);481            }482        })483    }484}485486decl_event!(487    pub enum Event<T>488    where489        AccountId = <T as system::Config>::AccountId,490    {491        /// New collection was created492        /// 493        /// # Arguments494        /// 495        /// * collection_id: Globally unique identifier of newly created collection.496        /// 497        /// * mode: [CollectionMode] converted into u8.498        /// 499        /// * account_id: Collection owner.500        Created(CollectionId, u8, AccountId),501502        /// New item was created.503        /// 504        /// # Arguments505        /// 506        /// * collection_id: Id of the collection where item was created.507        /// 508        /// * item_id: Id of an item. Unique within the collection.509        ///510        /// * recipient: Owner of newly created item 511        ItemCreated(CollectionId, TokenId, AccountId),512513        /// Collection item was burned.514        /// 515        /// # Arguments516        /// 517        /// collection_id.518        /// 519        /// item_id: Identifier of burned NFT.520        ItemDestroyed(CollectionId, TokenId),521522        /// Item was transferred523        ///524        /// * collection_id: Id of collection to which item is belong525        ///526        /// * item_id: Id of an item527        ///528        /// * sender: Original owner of item529        ///530        /// * recipient: New owner of item531        ///532        /// * amount: Always 1 for NFT533        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),534    }535);536537decl_module! {538    pub struct Module<T: Config> for enum Call 539    where 540        origin: T::Origin541    {542        fn deposit_event() = default;543        type Error = Error<T>;544545        fn on_initialize(now: T::BlockNumber) -> Weight {546547            if ChainVersion::get() < 2548            {549                let value = NextCollectionID::get();550                CreatedCollectionCount::put(value);551                ChainVersion::put(2);552            }553554            0555        }556557        /// 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.558        /// 559        /// # Permissions560        /// 561        /// * Anyone.562        /// 563        /// # Arguments564        /// 565        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.566        /// 567        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.568        /// 569        /// * token_prefix: UTF-8 string with token prefix.570        /// 571        /// * mode: [CollectionMode] collection type and type dependent data.572        // returns collection ID573        #[weight = <T as Config>::WeightInfo::create_collection()]574        pub fn create_collection(origin,575                                 collection_name: Vec<u16>,576                                 collection_description: Vec<u16>,577                                 token_prefix: Vec<u8>,578                                 mode: CollectionMode) -> DispatchResult {579580            // Anyone can create a collection581            let who = ensure_signed(origin)?;582583            let decimal_points = match mode {584                CollectionMode::Fungible(points) => points,585                _ => 0586            };587588            let chain_limit = ChainLimit::get();589590            // bound Total number of collections591            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);592593            // check params594            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);595            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);596            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);597            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);598599            // Generate next collection ID600            let next_id = CreatedCollectionCount::get()601                .checked_add(1)602                .ok_or(Error::<T>::NumOverflow)?;603604            // bound counter605            let total = CollectionCount::get()606                .checked_add(1)607                .ok_or(Error::<T>::NumOverflow)?;608609            CreatedCollectionCount::put(next_id);610            CollectionCount::put(total);611612            let limits = CollectionLimits {613                sponsored_data_size: chain_limit.custom_data_limit,614                ..Default::default()615            };616617            // Create new collection618            let new_collection = CollectionType {619                owner: who.clone(),620                name: collection_name,621                mode: mode.clone(),622                mint_mode: false,623                access: AccessMode::Normal,624                description: collection_description,625                decimal_points: decimal_points,626                token_prefix: token_prefix,627                offchain_schema: Vec::new(),628                schema_version: SchemaVersion::ImageURL,629                sponsor: T::AccountId::default(),630                sponsor_confirmed: false,631                variable_on_chain_schema: Vec::new(),632                const_on_chain_schema: Vec::new(),633                limits,634            };635636            // Add new collection to map637            <Collection<T>>::insert(next_id, new_collection);638639            // call event640            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));641642            Ok(())643        }644645        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.646        /// 647        /// # Permissions648        /// 649        /// * Collection Owner.650        /// 651        /// # Arguments652        /// 653        /// * collection_id: collection to destroy.654        #[weight = <T as Config>::WeightInfo::destroy_collection()]655        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {656657            let sender = ensure_signed(origin)?;658            Self::check_owner_permissions(collection_id, sender)?;659660            let target_collection = <Collection<T>>::get(collection_id);661            if !target_collection.limits.owner_can_destroy {662                fail!(Error::<T>::NoPermission);663            }664665            <AddressTokens<T>>::remove_prefix(collection_id);666            <Allowances<T>>::remove_prefix(collection_id);667            <Balance<T>>::remove_prefix(collection_id);668            <ItemListIndex>::remove(collection_id);669            <AdminList<T>>::remove(collection_id);670            <Collection<T>>::remove(collection_id);671            <WhiteList<T>>::remove_prefix(collection_id);672673            <NftItemList<T>>::remove_prefix(collection_id);674            <FungibleItemList<T>>::remove_prefix(collection_id);675            <ReFungibleItemList<T>>::remove_prefix(collection_id);676677            <NftTransferBasket<T>>::remove_prefix(collection_id);678            <FungibleTransferBasket<T>>::remove_prefix(collection_id);679            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);680681            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);682683            if CollectionCount::get() > 0684            {685                // bound couter686                let total = CollectionCount::get()687                    .checked_sub(1)688                    .ok_or(Error::<T>::NumOverflow)?;689690                CollectionCount::put(total);691            }692693            Ok(())694        }695696        /// Add an address to white list.697        /// 698        /// # Permissions699        /// 700        /// * Collection Owner701        /// * Collection Admin702        /// 703        /// # Arguments704        /// 705        /// * collection_id.706        /// 707        /// * address.708        #[weight = <T as Config>::WeightInfo::add_to_white_list()]709        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{710711            let sender = ensure_signed(origin)?;712            Self::check_owner_or_admin_permissions(collection_id, sender)?;713714            <WhiteList<T>>::insert(collection_id, address, true);715            716            Ok(())717        }718719        /// Remove an address from white list.720        /// 721        /// # Permissions722        /// 723        /// * Collection Owner724        /// * Collection Admin725        /// 726        /// # Arguments727        /// 728        /// * collection_id.729        /// 730        /// * address.731        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]732        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{733734            let sender = ensure_signed(origin)?;735            Self::check_owner_or_admin_permissions(collection_id, sender)?;736737            <WhiteList<T>>::remove(collection_id, address);738739            Ok(())740        }741742        /// Toggle between normal and white list access for the methods with access for `Anyone`.743        /// 744        /// # Permissions745        /// 746        /// * Collection Owner.747        /// 748        /// # Arguments749        /// 750        /// * collection_id.751        /// 752        /// * mode: [AccessMode]753        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]754        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult755        {756            let sender = ensure_signed(origin)?;757758            Self::check_owner_permissions(collection_id, sender)?;759            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();760            target_collection.access = mode;761            <Collection<T>>::insert(collection_id, target_collection);762763            Ok(())764        }765766        /// Allows Anyone to create tokens if:767        /// * White List is enabled, and768        /// * Address is added to white list, and769        /// * This method was called with True parameter770        /// 771        /// # Permissions772        /// * Collection Owner773        ///774        /// # Arguments775        /// 776        /// * collection_id.777        /// 778        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.779        #[weight = <T as Config>::WeightInfo::set_mint_permission()]780        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult781        {782            let sender = ensure_signed(origin)?;783784            Self::check_owner_permissions(collection_id, sender)?;785            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();786            target_collection.mint_mode = mint_permission;787            <Collection<T>>::insert(collection_id, target_collection);788789            Ok(())790        }791792        /// Change the owner of the collection.793        /// 794        /// # Permissions795        /// 796        /// * Collection Owner.797        /// 798        /// # Arguments799        /// 800        /// * collection_id.801        /// 802        /// * new_owner.803        #[weight = <T as Config>::WeightInfo::change_collection_owner()]804        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {805806            let sender = ensure_signed(origin)?;807            Self::check_owner_permissions(collection_id, sender)?;808            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();809            target_collection.owner = new_owner;810            <Collection<T>>::insert(collection_id, target_collection);811812            Ok(())813        }814815        /// Adds an admin of the Collection.816        /// 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. 817        /// 818        /// # Permissions819        /// 820        /// * Collection Owner.821        /// * Collection Admin.822        /// 823        /// # Arguments824        /// 825        /// * collection_id: ID of the Collection to add admin for.826        /// 827        /// * new_admin_id: Address of new admin to add.828        #[weight = <T as Config>::WeightInfo::add_collection_admin()]829        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {830831            let sender = ensure_signed(origin)?;832            Self::check_owner_or_admin_permissions(collection_id, sender)?;833            let mut admin_arr: Vec<T::AccountId> = Vec::new();834835            if <AdminList<T>>::contains_key(collection_id)836            {837                admin_arr = <AdminList<T>>::get(collection_id);838                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);839            }840841            // Number of collection admins842            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);843844            admin_arr.push(new_admin_id);845            <AdminList<T>>::insert(collection_id, admin_arr);846847            Ok(())848        }849850        /// 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.851        ///852        /// # Permissions853        /// 854        /// * Collection Owner.855        /// * Collection Admin.856        /// 857        /// # Arguments858        /// 859        /// * collection_id: ID of the Collection to remove admin for.860        /// 861        /// * account_id: Address of admin to remove.862        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]863        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {864865            let sender = ensure_signed(origin)?;866            Self::check_owner_or_admin_permissions(collection_id, sender)?;867            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);868869            let mut admin_arr = <AdminList<T>>::get(collection_id);870            admin_arr.retain(|i| *i != account_id);871            <AdminList<T>>::insert(collection_id, admin_arr);872873            Ok(())874        }875876        /// # Permissions877        /// 878        /// * Collection Owner879        /// 880        /// # Arguments881        /// 882        /// * collection_id.883        /// 884        /// * new_sponsor.885        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]886        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {887888            let sender = ensure_signed(origin)?;889890            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;891            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);892893            target_collection.sponsor = new_sponsor;894            target_collection.sponsor_confirmed = false;895            <Collection<T>>::insert(collection_id, target_collection);896897            Ok(())898        }899900        /// # Permissions901        /// 902        /// * Sponsor.903        /// 904        /// # Arguments905        /// 906        /// * collection_id.907        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]908        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {909910            let sender = ensure_signed(origin)?;911912            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;913            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);914915            target_collection.sponsor_confirmed = true;916            <Collection<T>>::insert(collection_id, target_collection);917918            Ok(())919        }920921        /// Switch back to pay-per-own-transaction model.922        ///923        /// # Permissions924        ///925        /// * Collection owner.926        /// 927        /// # Arguments928        /// 929        /// * collection_id.930        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]931        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {932933            let sender = ensure_signed(origin)?;934935            let mut target_collection = <Collection<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?;936            ensure!(sender == target_collection.owner, Error::<T>::NoPermission);937938            target_collection.sponsor = T::AccountId::default();939            target_collection.sponsor_confirmed = false;940            <Collection<T>>::insert(collection_id, target_collection);941942            Ok(())943        }944945        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.946        /// 947        /// # Permissions948        /// 949        /// * Collection Owner.950        /// * Collection Admin.951        /// * Anyone if952        ///     * White List is enabled, and953        ///     * Address is added to white list, and954        ///     * MintPermission is enabled (see SetMintPermission method)955        /// 956        /// # Arguments957        /// 958        /// * collection_id: ID of the collection.959        /// 960        /// * owner: Address, initial owner of the NFT.961        ///962        /// * data: Token data to store on chain.963        // #[weight =964        // (130_000_000 as Weight)965        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))966        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))967        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]968969        #[weight = <T as Config>::WeightInfo::create_item(data.len())]970        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {971972            let sender = ensure_signed(origin)?;973974            Self::collection_exists(collection_id)?;975976            let target_collection = <Collection<T>>::get(collection_id).unwrap();977978            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;979            Self::validate_create_item_args(&target_collection, &data)?;980            Self::create_item_no_validation(collection_id, owner, data)?;981982            Ok(())983        }984985        /// This method creates multiple instances of NFT Collection created with CreateCollection method.986        /// 987        /// # Permissions988        /// 989        /// * Collection Owner.990        /// * Collection Admin.991        /// * Anyone if992        ///     * White List is enabled, and993        ///     * Address is added to white list, and994        ///     * MintPermission is enabled (see SetMintPermission method)995        /// 996        /// # Arguments997        /// 998        /// * collection_id: ID of the collection.999        /// 1000        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1001        /// 1002        /// * owner: Address, initial owner of the NFT.1003        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1004                               .map(|data| { data.len() })1005                               .sum())]1006        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10071008            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1009            let sender = ensure_signed(origin)?;10101011            Self::collection_exists(collection_id)?;1012            let target_collection = <Collection<T>>::get(collection_id).unwrap();10131014            Self::can_create_items_in_collection(collection_id, &target_collection, &sender, &owner)?;10151016            for data in &items_data {1017                Self::validate_create_item_args(&target_collection, data)?;1018            }1019            for data in &items_data {1020                Self::create_item_no_validation(collection_id, owner.clone(), data.clone())?;1021            }10221023            Ok(())1024        }10251026        /// Destroys a concrete instance of NFT.1027        /// 1028        /// # Permissions1029        /// 1030        /// * Collection Owner.1031        /// * Collection Admin.1032        /// * Current NFT Owner.1033        /// 1034        /// # Arguments1035        /// 1036        /// * collection_id: ID of the collection.1037        /// 1038        /// * item_id: ID of NFT to burn.1039        #[weight = <T as Config>::WeightInfo::burn_item()]1040        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10411042            let sender = ensure_signed(origin)?;1043            Self::collection_exists(collection_id)?;10441045            // Transfer permissions check1046            let target_collection = <Collection<T>>::get(collection_id).unwrap();1047            ensure!(1048                Self::is_item_owner(sender.clone(), collection_id, item_id) ||1049                (1050                    target_collection.limits.owner_can_transfer &&1051                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1052                ),1053                Error::<T>::NoPermission1054            );10551056            if target_collection.access == AccessMode::WhiteList {1057                Self::check_white_list(collection_id, &sender)?;1058            }10591060            match target_collection.mode1061            {1062                CollectionMode::NFT => Self::burn_nft_item(collection_id, item_id)?,1063                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, collection_id, value)?,1064                CollectionMode::ReFungible  => Self::burn_refungible_item(collection_id, item_id, &sender)?,1065                _ => ()1066            };10671068            // call event1069            Self::deposit_event(RawEvent::ItemDestroyed(collection_id, item_id));10701071            Ok(())1072        }10731074        /// Change ownership of the token.1075        /// 1076        /// # Permissions1077        /// 1078        /// * Collection Owner1079        /// * Collection Admin1080        /// * Current NFT owner1081        ///1082        /// # Arguments1083        /// 1084        /// * recipient: Address of token recipient.1085        /// 1086        /// * collection_id.1087        /// 1088        /// * item_id: ID of the item1089        ///     * Non-Fungible Mode: Required.1090        ///     * Fungible Mode: Ignored.1091        ///     * Re-Fungible Mode: Required.1092        /// 1093        /// * value: Amount to transfer.1094        ///     * Non-Fungible Mode: Ignored1095        ///     * Fungible Mode: Must specify transferred amount1096        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1097        #[weight = <T as Config>::WeightInfo::transfer()]1098        pub fn transfer(origin, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1099            let sender = ensure_signed(origin)?;1100            Self::transfer_internal(sender, recipient, collection_id, item_id, value)1101        }11021103        /// Set, change, or remove approved address to transfer the ownership of the NFT.1104        /// 1105        /// # Permissions1106        /// 1107        /// * Collection Owner1108        /// * Collection Admin1109        /// * Current NFT owner1110        /// 1111        /// # Arguments1112        /// 1113        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1114        /// 1115        /// * collection_id.1116        /// 1117        /// * item_id: ID of the item.1118        #[weight = <T as Config>::WeightInfo::approve()]1119        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11201121            let sender = ensure_signed(origin)?;11221123            Self::collection_exists(collection_id)?;1124            Self::token_exists(collection_id, item_id, &sender)?;11251126            // Transfer permissions check1127            let target_collection = <Collection<T>>::get(collection_id).unwrap();1128            let allowance_limit = if (1129                target_collection.limits.owner_can_transfer &&1130                Self::is_owner_or_admin_permissions(1131                    collection_id,1132                    sender.clone(),1133                )1134            ) {1135                None1136            } else if let Some(amount) = Self::owned_amount(1137                sender.clone(),1138                collection_id,1139                item_id,1140            ) {1141                Some(amount)1142            } else {1143                fail!(Error::<T>::NoPermission);1144            };11451146            if target_collection.access == AccessMode::WhiteList {1147                Self::check_white_list(collection_id, &sender)?;1148                Self::check_white_list(collection_id, &spender)?;1149            }11501151            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1152            let mut allowance: u128 = amount;1153            if allowance_exists {1154                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1155            }1156            if let Some(limit) = allowance_limit {1157                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1158            }1159            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11601161            Ok(())1162        }1163        1164        /// 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.1165        /// 1166        /// # Permissions1167        /// * Collection Owner1168        /// * Collection Admin1169        /// * Current NFT owner1170        /// * Address approved by current NFT owner1171        /// 1172        /// # Arguments1173        /// 1174        /// * from: Address that owns token.1175        /// 1176        /// * recipient: Address of token recipient.1177        /// 1178        /// * collection_id.1179        /// 1180        /// * item_id: ID of the item.1181        /// 1182        /// * value: Amount to transfer.1183        #[weight = <T as Config>::WeightInfo::transfer_from()]1184        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11851186            let sender = ensure_signed(origin)?;1187            let mut appoved_transfer = false;11881189            // Check approval1190            let mut approval: u128 = 0;1191            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1192                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1193                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1194                appoved_transfer = true;1195            }11961197            let target_collection = <Collection<T>>::get(collection_id).unwrap();11981199            // Limits check1200            Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;12011202            // Transfer permissions check         1203            ensure!(1204                appoved_transfer || 1205                (1206                    target_collection.limits.owner_can_transfer &&1207                    Self::is_owner_or_admin_permissions(collection_id, sender.clone())1208                ),1209                Error::<T>::NoPermission1210            );12111212            if target_collection.access == AccessMode::WhiteList {1213                Self::check_white_list(collection_id, &sender)?;1214                Self::check_white_list(collection_id, &recipient)?;1215            }12161217            // Reduce approval by transferred amount or remove if remaining approval drops to 01218            if approval.checked_sub(value).unwrap_or(0) > 0 {1219                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1220            }1221            else {1222                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1223            }12241225            match target_collection.mode1226            {1227                CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, from, recipient)?,1228                CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &from, &recipient)?,1229                CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, from.clone(), recipient)?,1230                _ => ()1231            };12321233            Ok(())1234        }12351236        // #[weight = 0]1237        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12381239        //     // let no_perm_mes = "You do not have permissions to modify this collection";1240        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1241        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1242        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12431244        //     // // on_nft_received  call12451246        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12471248        //     Ok(())1249        // }12501251        /// Set off-chain data schema.1252        /// 1253        /// # Permissions1254        /// 1255        /// * Collection Owner1256        /// * Collection Admin1257        /// 1258        /// # Arguments1259        /// 1260        /// * collection_id.1261        /// 1262        /// * schema: String representing the offchain data schema.1263        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1264        pub fn set_variable_meta_data (1265            origin,1266            collection_id: CollectionId,1267            item_id: TokenId,1268            data: Vec<u8>1269        ) -> DispatchResult {1270            let sender = ensure_signed(origin)?;1271            1272            Self::collection_exists(collection_id)?;1273            Self::token_exists(collection_id, item_id, &sender)?;12741275            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12761277            // Modify permissions check1278            let target_collection = <Collection<T>>::get(collection_id).unwrap();1279            ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1280                Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1281                Error::<T>::NoPermission);12821283            match target_collection.mode1284            {1285                CollectionMode::NFT => Self::set_nft_variable_data(collection_id, item_id, data)?,1286                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(collection_id, item_id, data)?,1287                CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1288                _ => fail!(Error::<T>::UnexpectedCollectionType)1289            };12901291            Ok(())1292        }1293 1294        /// Set schema standard1295        /// ImageURL1296        /// Unique1297        /// 1298        /// # Permissions1299        /// 1300        /// * Collection Owner1301        /// * Collection Admin1302        /// 1303        /// # Arguments1304        /// 1305        /// * collection_id.1306        /// 1307        /// * schema: SchemaVersion: enum1308        #[weight = <T as Config>::WeightInfo::set_schema_version()]1309        pub fn set_schema_version(1310            origin,1311            collection_id: CollectionId,1312            version: SchemaVersion1313        ) -> DispatchResult {1314            let sender = ensure_signed(origin)?;1315            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;1316            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1317            target_collection.schema_version = version;1318            <Collection<T>>::insert(collection_id, target_collection);13191320            Ok(())1321        }13221323        /// Set off-chain data schema.1324        /// 1325        /// # Permissions1326        /// 1327        /// * Collection Owner1328        /// * Collection Admin1329        /// 1330        /// # Arguments1331        /// 1332        /// * collection_id.1333        /// 1334        /// * schema: String representing the offchain data schema.1335        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1336        pub fn set_offchain_schema(1337            origin,1338            collection_id: CollectionId,1339            schema: Vec<u8>1340        ) -> DispatchResult {1341            let sender = ensure_signed(origin)?;1342            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13431344            // check schema limit1345            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13461347            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1348            target_collection.offchain_schema = schema;1349            <Collection<T>>::insert(collection_id, target_collection);13501351            Ok(())1352        }13531354        /// Set const on-chain data schema.1355        /// 1356        /// # Permissions1357        /// 1358        /// * Collection Owner1359        /// * Collection Admin1360        /// 1361        /// # Arguments1362        /// 1363        /// * collection_id.1364        /// 1365        /// * schema: String representing the const on-chain data schema.1366        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1367        pub fn set_const_on_chain_schema (1368            origin,1369            collection_id: CollectionId,1370            schema: Vec<u8>1371        ) -> DispatchResult {1372            let sender = ensure_signed(origin)?;1373            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;13741375            // check schema limit1376            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13771378            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1379            target_collection.const_on_chain_schema = schema;1380            <Collection<T>>::insert(collection_id, target_collection);13811382            Ok(())1383        }13841385        /// Set variable on-chain data schema.1386        /// 1387        /// # Permissions1388        /// 1389        /// * Collection Owner1390        /// * Collection Admin1391        /// 1392        /// # Arguments1393        /// 1394        /// * collection_id.1395        /// 1396        /// * schema: String representing the variable on-chain data schema.1397        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1398        pub fn set_variable_on_chain_schema (1399            origin,1400            collection_id: CollectionId,1401            schema: Vec<u8>1402        ) -> DispatchResult {1403            let sender = ensure_signed(origin)?;1404            Self::check_owner_or_admin_permissions(collection_id, sender.clone())?;14051406            // check schema limit1407            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14081409            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1410            target_collection.variable_on_chain_schema = schema;1411            <Collection<T>>::insert(collection_id, target_collection);14121413            Ok(())1414        }14151416        // Sudo permissions function1417        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1418        pub fn set_chain_limits(1419            origin,1420            limits: ChainLimits1421        ) -> DispatchResult {14221423            #[cfg(not(feature = "runtime-benchmarks"))]1424            ensure_root(origin)?;14251426            <ChainLimit>::put(limits);1427            Ok(())1428        }14291430        /// Enable smart contract self-sponsoring.1431        /// 1432        /// # Permissions1433        /// 1434        /// * Contract Owner1435        /// 1436        /// # Arguments1437        /// 1438        /// * contract address1439        /// * enable flag1440        /// 1441        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1442        pub fn enable_contract_sponsoring(1443            origin,1444            contract_address: T::AccountId,1445            enable: bool1446        ) -> DispatchResult {14471448            let sender = ensure_signed(origin)?;14491450            #[cfg(feature = "runtime-benchmarks")]1451            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14521453            Self::ensure_contract_owned(sender, &contract_address)?;14541455            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1456            Ok(())1457        }14581459        /// Set the rate limit for contract sponsoring to specified number of blocks.1460        /// 1461        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1462        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1463        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1464        /// from contract endowment if there are at least B blocks between such transactions. 1465        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1466        /// 1467        /// # Permissions1468        /// 1469        /// * Contract Owner1470        /// 1471        /// # Arguments1472        /// 1473        /// -`contract_address`: Address of the contract to sponsor1474        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1475        /// 1476        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1477        pub fn set_contract_sponsoring_rate_limit(1478            origin,1479            contract_address: T::AccountId,1480            rate_limit: T::BlockNumber1481        ) -> DispatchResult {1482            let sender = ensure_signed(origin)?;14831484            #[cfg(feature = "runtime-benchmarks")]1485            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());14861487            Self::ensure_contract_owned(sender, &contract_address)?;1488            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1489            Ok(())1490        }14911492        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Address that deployed smart contract.1497        /// 1498        /// # Arguments1499        /// 1500        /// -`contract_address`: Address of the contract.1501        /// 1502        /// - `enable`: .  1503        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1504        pub fn toggle_contract_white_list(1505            origin,1506            contract_address: T::AccountId,1507            enable: bool1508        ) -> DispatchResult {1509            let sender = ensure_signed(origin)?;15101511            #[cfg(feature = "runtime-benchmarks")]1512            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15131514            Self::ensure_contract_owned(sender, &contract_address)?;1515            <ContractWhiteListEnabled<T>>::insert(contract_address, enable);1516            Ok(())1517        }1518        1519        /// Add an address to smart contract white list.1520        /// 1521        /// # Permissions1522        /// 1523        /// * Address that deployed smart contract.1524        /// 1525        /// # Arguments1526        /// 1527        /// -`contract_address`: Address of the contract.1528        ///1529        /// -`account_address`: Address to add.1530        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1531        pub fn add_to_contract_white_list(1532            origin,1533            contract_address: T::AccountId,1534            account_address: T::AccountId1535        ) -> DispatchResult {1536            let sender = ensure_signed(origin)?;15371538            #[cfg(feature = "runtime-benchmarks")]1539            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1540            1541            Self::ensure_contract_owned(sender, &contract_address)?;      1542            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1543            Ok(())1544        }15451546        /// Remove an address from smart contract white list.1547        /// 1548        /// # Permissions1549        /// 1550        /// * Address that deployed smart contract.1551        /// 1552        /// # Arguments1553        /// 1554        /// -`contract_address`: Address of the contract.1555        ///1556        /// -`account_address`: Address to remove.1557        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1558        pub fn remove_from_contract_white_list(1559            origin,1560            contract_address: T::AccountId,1561            account_address: T::AccountId1562        ) -> DispatchResult {1563            let sender = ensure_signed(origin)?;15641565            #[cfg(feature = "runtime-benchmarks")]1566            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15671568            Self::ensure_contract_owned(sender, &contract_address)?;1569            <ContractWhiteList<T>>::remove(contract_address, account_address);1570            Ok(())1571        }15721573        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1574        pub fn set_collection_limits(1575            origin,1576            collection_id: u32,1577            new_limits: CollectionLimits<T::BlockNumber>,1578        ) -> DispatchResult {1579            let sender = ensure_signed(origin)?;1580            Self::check_owner_permissions(collection_id, sender.clone())?;1581            let mut target_collection = <Collection<T>>::get(collection_id).unwrap();1582            let old_limits = target_collection.limits;1583            let chain_limits = ChainLimit::get();15841585            // collection bounds1586            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1587                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1588                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1589                Error::<T>::CollectionLimitBoundsExceeded);15901591            // token_limit   check  prev1592            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1593            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);15941595            ensure!(1596                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1597                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1598                Error::<T>::OwnerPermissionsCantBeReverted,1599            );16001601            target_collection.limits = new_limits;1602            <Collection<T>>::insert(collection_id, target_collection);16031604            Ok(())1605        } 1606    }1607}16081609impl<T: Config> Module<T> {16101611    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {16121613        let target_collection = <Collection<T>>::get(collection_id).unwrap();16141615        // Limits check1616        Self::is_correct_transfer(collection_id, &target_collection, &recipient)?;16171618        // Transfer permissions check1619        ensure!(Self::is_item_owner(sender.clone(), collection_id, item_id) ||1620            Self::is_owner_or_admin_permissions(collection_id, sender.clone()),1621            Error::<T>::NoPermission);16221623        if target_collection.access == AccessMode::WhiteList {1624            Self::check_white_list(collection_id, &sender)?;1625            Self::check_white_list(collection_id, &recipient)?;1626        }16271628        match target_collection.mode1629        {1630            CollectionMode::NFT => Self::transfer_nft(collection_id, item_id, sender.clone(), recipient.clone())?,1631            CollectionMode::Fungible(_)  => Self::transfer_fungible(collection_id, value, &sender, &recipient)?,1632            CollectionMode::ReFungible  => Self::transfer_refungible(collection_id, item_id, value, sender.clone(), recipient.clone())?,1633            _ => ()1634        };16351636        Self::deposit_event(RawEvent::Transfer(collection_id, item_id, sender, recipient, value));16371638        Ok(())1639    }164016411642    fn is_correct_transfer(collection_id: CollectionId, collection: &CollectionType<T>, recipient: &T::AccountId) -> DispatchResult {16431644        // check token limit and account token limit1645        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1646        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1647        1648        Ok(())1649    }16501651    fn can_create_items_in_collection(collection_id: CollectionId, collection: &CollectionType<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {16521653        // check token limit and account token limit1654        let total_items: u32 = ItemListIndex::get(collection_id);1655        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1656        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1657        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16581659        if !Self::is_owner_or_admin_permissions(collection_id, sender.clone()) {1660            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1661            Self::check_white_list(collection_id, owner)?;1662            Self::check_white_list(collection_id, sender)?;1663        }16641665        Ok(())1666    }16671668    fn validate_create_item_args(target_collection: &CollectionType<T>, data: &CreateItemData) -> DispatchResult {1669        match target_collection.mode1670        {1671            CollectionMode::NFT => {1672                if let CreateItemData::NFT(data) = data {1673                    // check sizes1674                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1675                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1676                } else {1677                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1678                }1679            },1680            CollectionMode::Fungible(_) => {1681                if let CreateItemData::Fungible(_) = data {1682                } else {1683                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1684                }1685            },1686            CollectionMode::ReFungible => {1687                if let CreateItemData::ReFungible(data) = data {16881689                    // check sizes1690                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1691                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16921693                    // Check refungibility limits1694                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1695                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1696                } else {1697                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1698                }1699            },1700            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1701        };17021703        Ok(())1704    }17051706    fn create_item_no_validation(collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1707        match data1708        {1709            CreateItemData::NFT(data) => {1710                let item = NftItemType {1711                    owner: owner.clone(),1712                    const_data: data.const_data,1713                    variable_data: data.variable_data1714                };17151716                Self::add_nft_item(collection_id, item)?;1717            },1718            CreateItemData::Fungible(data) => {1719                Self::add_fungible_item(collection_id, &owner, data.value)?;1720            },1721            CreateItemData::ReFungible(data) => {1722                let mut owner_list = Vec::new();1723                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17241725                let item = ReFungibleItemType {1726                    owner: owner_list,1727                    const_data: data.const_data,1728                    variable_data: data.variable_data1729                };17301731                Self::add_refungible_item(collection_id, item)?;1732            }1733        };17341735        // call event1736        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17371738        Ok(())1739    }17401741    fn add_fungible_item(collection_id: CollectionId, owner: &T::AccountId, value: u128) -> DispatchResult {17421743        // Does new owner already have an account?1744        let mut balance: u128 = 0;1745        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1746            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1747        } 17481749        // Mint 1750        let item = FungibleItemType {1751            value: balance + value1752        };1753        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17541755        // Update balance1756        let new_balance = <Balance<T>>::get(collection_id, owner)1757            .checked_add(value)1758            .ok_or(Error::<T>::NumOverflow)?;1759        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17601761        Ok(())1762    }17631764    fn add_refungible_item(collection_id: CollectionId, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1765        let current_index = <ItemListIndex>::get(collection_id)1766            .checked_add(1)1767            .ok_or(Error::<T>::NumOverflow)?;1768        let itemcopy = item.clone();17691770        let value = item.owner.first().unwrap().fraction;1771        let owner = item.owner.first().unwrap().owner.clone();17721773        Self::add_token_index(collection_id, current_index, &owner)?;17741775        <ItemListIndex>::insert(collection_id, current_index);1776        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17771778        // Update balance1779        let new_balance = <Balance<T>>::get(collection_id, &owner)1780            .checked_add(value)1781            .ok_or(Error::<T>::NumOverflow)?;1782        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17831784        Ok(())1785    }17861787    fn add_nft_item(collection_id: CollectionId, item: NftItemType<T::AccountId>) -> DispatchResult {1788        let current_index = <ItemListIndex>::get(collection_id)1789            .checked_add(1)1790            .ok_or(Error::<T>::NumOverflow)?;17911792        let item_owner = item.owner.clone();1793        Self::add_token_index(collection_id, current_index, &item.owner)?;17941795        <ItemListIndex>::insert(collection_id, current_index);1796        <NftItemList<T>>::insert(collection_id, current_index, item);17971798        // Update balance1799        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1800            .checked_add(1)1801            .ok_or(Error::<T>::NumOverflow)?;1802        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18031804        Ok(())1805    }18061807    fn burn_refungible_item(1808        collection_id: CollectionId,1809        item_id: TokenId,1810        owner: &T::AccountId,1811    ) -> DispatchResult {1812        ensure!(1813            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1814            Error::<T>::TokenNotFound1815        );1816        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1817        let rft_balance = token1818            .owner1819            .iter()1820            .filter(|&i| i.owner == *owner)1821            .next()1822            .unwrap();1823        Self::remove_token_index(collection_id, item_id, owner)?;18241825        // update balance1826        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1827            .checked_sub(rft_balance.fraction)1828            .ok_or(Error::<T>::NumOverflow)?;1829        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18301831        // Re-create owners list with sender removed1832        let index = token1833            .owner1834            .iter()1835            .position(|i| i.owner == *owner)1836            .unwrap();1837        token.owner.remove(index);1838        let owner_count = token.owner.len();18391840        // Burn the token completely if this was the last (only) owner1841        if owner_count == 0 {1842            <ReFungibleItemList<T>>::remove(collection_id, item_id);1843            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1844        }1845        else {1846            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1847        }18481849        Ok(())1850    }18511852    fn burn_nft_item(collection_id: CollectionId, item_id: TokenId) -> DispatchResult {1853        ensure!(1854            <NftItemList<T>>::contains_key(collection_id, item_id),1855            Error::<T>::TokenNotFound1856        );1857        let item = <NftItemList<T>>::get(collection_id, item_id);1858        Self::remove_token_index(collection_id, item_id, &item.owner)?;18591860        // update balance1861        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1862            .checked_sub(1)1863            .ok_or(Error::<T>::NumOverflow)?;1864        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1865        <NftItemList<T>>::remove(collection_id, item_id);1866        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18671868        Ok(())1869    }18701871    fn burn_fungible_item(owner: &T::AccountId, collection_id: CollectionId, value: u128) -> DispatchResult {1872        ensure!(1873            <FungibleItemList<T>>::contains_key(collection_id, owner),1874            Error::<T>::TokenNotFound1875        );1876        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1877        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18781879        // update balance1880        let new_balance = <Balance<T>>::get(collection_id, owner)1881            .checked_sub(value)1882            .ok_or(Error::<T>::NumOverflow)?;1883        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18841885        if balance.value - value > 0 {1886            balance.value -= value;1887            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1888        }1889        else {1890            <FungibleItemList<T>>::remove(collection_id, owner);1891        }18921893        Ok(())1894    }18951896    fn collection_exists(collection_id: CollectionId) -> DispatchResult {1897        ensure!(1898            <Collection<T>>::contains_key(collection_id),1899            Error::<T>::CollectionNotFound1900        );1901        Ok(())1902    }19031904    fn check_owner_permissions(collection_id: CollectionId, subject: T::AccountId) -> DispatchResult {1905        Self::collection_exists(collection_id)?;19061907        let target_collection = <Collection<T>>::get(collection_id).unwrap();1908        ensure!(1909            subject == target_collection.owner,1910            Error::<T>::NoPermission1911        );19121913        Ok(())1914    }19151916    fn is_owner_or_admin_permissions(collection_id: CollectionId, subject: T::AccountId) -> bool {1917        let target_collection = <Collection<T>>::get(collection_id).unwrap();1918        let mut result: bool = subject == target_collection.owner;1919        let exists = <AdminList<T>>::contains_key(collection_id);19201921        if !result & exists {1922            if <AdminList<T>>::get(collection_id).contains(&subject) {1923                result = true1924            }1925        }19261927        result1928    }19291930    fn check_owner_or_admin_permissions(1931        collection_id: CollectionId,1932        subject: T::AccountId,1933    ) -> DispatchResult {1934        Self::collection_exists(collection_id)?;1935        let result = Self::is_owner_or_admin_permissions(collection_id, subject.clone());19361937        ensure!(1938            result,1939            Error::<T>::NoPermission1940        );1941        Ok(())1942    }19431944    fn owned_amount(1945        subject: T::AccountId,1946        collection_id: CollectionId,1947        item_id: TokenId,1948    ) -> Option<u128> {1949        let target_collection = <Collection<T>>::get(collection_id);19501951        match target_collection.mode {1952            CollectionMode::NFT => {1953                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1954                    return Some(1)1955                }1956                None1957            },1958            CollectionMode::Fungible(_) => {1959                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1960                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1961                        .value);1962                }1963                None1964            },1965            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1966                .owner1967                .iter()1968                .find(|i| i.owner == subject)1969                .map(|i| i.fraction),1970            CollectionMode::Invalid => None,1971        }1972    }19731974    fn is_item_owner(subject: T::AccountId, collection_id: CollectionId, item_id: TokenId) -> bool {1975        let target_collection = <Collection<T>>::get(collection_id).unwrap();19761977        match target_collection.mode {1978            CollectionMode::NFT => {1979                <NftItemList<T>>::get(collection_id, item_id).owner == subject1980            }1981            CollectionMode::Fungible(_) => {1982                <FungibleItemList<T>>::contains_key(collection_id, &subject)1983            }1984            CollectionMode::ReFungible => {1985                <ReFungibleItemList<T>>::get(collection_id, item_id)1986                    .owner1987                    .iter()1988                    .any(|i| i.owner == subject)1989            }1990            CollectionMode::Invalid => false,1991        }1992    }19931994    fn check_white_list(collection_id: CollectionId, address: &T::AccountId) -> DispatchResult {1995        let mes = Error::<T>::AddresNotInWhiteList;1996        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);19971998        Ok(())1999    }20002001    /// Check if token exists. In case of Fungible, check if there is an entry for 2002    /// the owner in fungible balances double map2003    fn token_exists(2004        collection_id: CollectionId,2005        item_id: TokenId,2006        owner: &T::AccountId2007    ) -> DispatchResult {2008        let target_collection = <Collection<T>>::get(collection_id).unwrap();2009        let exists = match target_collection.mode2010        {2011            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2012            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2013            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2014            _ => false2015        };20162017        ensure!(exists == true, Error::<T>::TokenNotFound);2018        Ok(())2019    }20202021    fn transfer_fungible(2022        collection_id: CollectionId,2023        value: u128,2024        owner: &T::AccountId,2025        recipient: &T::AccountId,2026    ) -> DispatchResult {2027        Self::token_exists(collection_id, 0, owner)?;20282029        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2030        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20312032        // Send balance to recipient (updates balanceOf of recipient)2033        Self::add_fungible_item(collection_id, recipient, value)?;20342035        // update balanceOf of sender2036        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20372038        // Reduce or remove sender2039        if balance.value == value {2040            <FungibleItemList<T>>::remove(collection_id, owner);2041        }2042        else {2043            balance.value -= value;2044            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2045        }20462047        Ok(())2048    }20492050    fn transfer_refungible(2051        collection_id: CollectionId,2052        item_id: TokenId,2053        value: u128,2054        owner: T::AccountId,2055        new_owner: T::AccountId,2056    ) -> DispatchResult {2057        Self::token_exists(collection_id, item_id, &owner)?;20582059        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2060        let item = full_item2061            .owner2062            .iter()2063            .filter(|i| i.owner == owner)2064            .next()2065            .ok_or(Error::<T>::NumOverflow)?;2066        let amount = item.fraction;20672068        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20692070        // update balance2071        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2072            .checked_sub(value)2073            .ok_or(Error::<T>::NumOverflow)?;2074        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20752076        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2077            .checked_add(value)2078            .ok_or(Error::<T>::NumOverflow)?;2079        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20802081        let old_owner = item.owner.clone();2082        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20832084        // transfer2085        if amount == value && !new_owner_has_account {2086            // change owner2087            // new owner do not have account2088            let mut new_full_item = full_item.clone();2089            new_full_item2090                .owner2091                .iter_mut()2092                .find(|i| i.owner == owner)2093                .unwrap()2094                .owner = new_owner.clone();2095            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);20962097            // update index collection2098            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2099        } else {2100            let mut new_full_item = full_item.clone();2101            new_full_item2102                .owner2103                .iter_mut()2104                .find(|i| i.owner == owner)2105                .unwrap()2106                .fraction -= value;21072108            // separate amount2109            if new_owner_has_account {2110                // new owner has account2111                new_full_item2112                    .owner2113                    .iter_mut()2114                    .find(|i| i.owner == new_owner)2115                    .unwrap()2116                    .fraction += value;2117            } else {2118                // new owner do not have account2119                new_full_item.owner.push(Ownership {2120                    owner: new_owner.clone(),2121                    fraction: value,2122                });2123                Self::add_token_index(collection_id, item_id, &new_owner)?;2124            }21252126            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2127        }21282129        Ok(())2130    }21312132    fn transfer_nft(2133        collection_id: CollectionId,2134        item_id: TokenId,2135        sender: T::AccountId,2136        new_owner: T::AccountId,2137    ) -> DispatchResult {2138        Self::token_exists(collection_id, item_id, &sender)?;21392140        let mut item = <NftItemList<T>>::get(collection_id, item_id);21412142        ensure!(2143            sender == item.owner,2144            Error::<T>::MustBeTokenOwner2145        );21462147        // update balance2148        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2149            .checked_sub(1)2150            .ok_or(Error::<T>::NumOverflow)?;2151        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21522153        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2154            .checked_add(1)2155            .ok_or(Error::<T>::NumOverflow)?;2156        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21572158        // change owner2159        let old_owner = item.owner.clone();2160        item.owner = new_owner.clone();2161        <NftItemList<T>>::insert(collection_id, item_id, item);21622163        // update index collection2164        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21652166        Ok(())2167    }2168    2169    fn set_re_fungible_variable_data(2170        collection_id: CollectionId,2171        item_id: TokenId,2172        data: Vec<u8>2173    ) -> DispatchResult {2174        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21752176        item.variable_data = data;21772178        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21792180        Ok(())2181    }21822183    fn set_nft_variable_data(2184        collection_id: CollectionId,2185        item_id: TokenId,2186        data: Vec<u8>2187    ) -> DispatchResult {2188        let mut item = <NftItemList<T>>::get(collection_id, item_id);2189        2190        item.variable_data = data;21912192        <NftItemList<T>>::insert(collection_id, item_id, item);2193        2194        Ok(())2195    }21962197    fn init_collection(item: &CollectionType<T>) {2198        // check params2199        assert!(2200            item.decimal_points <= MAX_DECIMAL_POINTS,2201            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2202        );2203        assert!(2204            item.name.len() <= 64,2205            "Collection name can not be longer than 63 char"2206        );2207        assert!(2208            item.name.len() <= 256,2209            "Collection description can not be longer than 255 char"2210        );2211        assert!(2212            item.token_prefix.len() <= 16,2213            "Token prefix can not be longer than 15 char"2214        );22152216        // Generate next collection ID2217        let next_id = CreatedCollectionCount::get()2218            .checked_add(1)2219            .unwrap();22202221        CreatedCollectionCount::put(next_id);2222    }22232224    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2225        let current_index = <ItemListIndex>::get(collection_id)2226            .checked_add(1)2227            .unwrap();22282229        let item_owner = item.owner.clone();2230        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22312232        <ItemListIndex>::insert(collection_id, current_index);22332234        // Update balance2235        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2236            .checked_add(1)2237            .unwrap();2238        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2239    }22402241    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2242        let current_index = <ItemListIndex>::get(collection_id)2243            .checked_add(1)2244            .unwrap();22452246        Self::add_token_index(collection_id, current_index, owner).unwrap();22472248        <ItemListIndex>::insert(collection_id, current_index);22492250        // Update balance2251        let new_balance = <Balance<T>>::get(collection_id, owner)2252            .checked_add(item.value)2253            .unwrap();2254        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2255    }22562257    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2258        let current_index = <ItemListIndex>::get(collection_id)2259            .checked_add(1)2260            .unwrap();22612262        let value = item.owner.first().unwrap().fraction;2263        let owner = item.owner.first().unwrap().owner.clone();22642265        Self::add_token_index(collection_id, current_index, &owner).unwrap();22662267        <ItemListIndex>::insert(collection_id, current_index);22682269        // Update balance2270        let new_balance = <Balance<T>>::get(collection_id, &owner)2271            .checked_add(value)2272            .unwrap();2273        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2274    }22752276    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {22772278        // add to account limit2279        if <AccountItemCount<T>>::contains_key(owner) {22802281            // bound Owned tokens by a single address2282            let count = <AccountItemCount<T>>::get(owner);2283            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22842285            <AccountItemCount<T>>::insert(owner.clone(), count2286                .checked_add(1)2287                .ok_or(Error::<T>::NumOverflow)?);2288        }2289        else {2290            <AccountItemCount<T>>::insert(owner.clone(), 1);2291        }22922293        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2294        if list_exists {2295            let mut list = <AddressTokens<T>>::get(collection_id, owner);2296            let item_contains = list.contains(&item_index.clone());22972298            if !item_contains {2299                list.push(item_index.clone());2300            }23012302            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2303        } else {2304            let mut itm = Vec::new();2305            itm.push(item_index.clone());2306            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2307        }23082309        Ok(())2310    }23112312    fn remove_token_index(2313        collection_id: CollectionId,2314        item_index: TokenId,2315        owner: &T::AccountId,2316    ) -> DispatchResult {23172318        // update counter2319        <AccountItemCount<T>>::insert(owner.clone(), 2320            <AccountItemCount<T>>::get(owner)2321            .checked_sub(1)2322            .ok_or(Error::<T>::NumOverflow)?);232323242325        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2326        if list_exists {2327            let mut list = <AddressTokens<T>>::get(collection_id, owner);2328            let item_contains = list.contains(&item_index.clone());23292330            if item_contains {2331                list.retain(|&item| item != item_index);2332                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2333            }2334        }23352336        Ok(())2337    }23382339    fn move_token_index(2340        collection_id: CollectionId,2341        item_index: TokenId,2342        old_owner: &T::AccountId,2343        new_owner: &T::AccountId,2344    ) -> DispatchResult {2345        Self::remove_token_index(collection_id, item_index, old_owner)?;2346        Self::add_token_index(collection_id, item_index, new_owner)?;23472348        Ok(())2349    }2350    2351    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2352        if <ContractOwner<T>>::contains_key(contract.clone()) {2353            let owner = <ContractOwner<T>>::get(contract);2354            ensure!(account == owner, Error::<T>::NoPermission);2355        } else {2356            fail!(Error::<T>::NoPermission);2357        }23582359        Ok(())2360    }2361}23622363////////////////////////////////////////////////////////////////////////////////////////////////////2364// Economic models2365// #region23662367/// Fee multiplier.2368pub type Multiplier = FixedU128;23692370type BalanceOf<T> = <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::Balance;23712372/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2373/// in the queue.2374#[derive(Encode, Decode, Clone, Eq, PartialEq)]2375pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23762377impl<T: Config + Send + Sync> sp_std::fmt::Debug 2378    for ChargeTransactionPayment<T>2379{2380	#[cfg(feature = "std")]2381	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2382		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2383	}2384	#[cfg(not(feature = "std"))]2385	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2386		Ok(())2387	}2388}23892390impl<T: Config> ChargeTransactionPayment<T>2391where2392    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2393    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2394    T::AccountId: AsRef<[u8]>,2395    T::AccountId: UncheckedFrom<T::Hash>,2396{2397    fn traditional_fee(2398        len: usize,2399        info: &DispatchInfoOf<T::Call>,2400        tip: BalanceOf<T>,2401    ) -> BalanceOf<T>2402    where2403        T::Call: Dispatchable<Info = DispatchInfo>,2404    {2405        <transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2406    }24072408	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2409        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2410        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2411        let len_saturation = max_block_length as u64 / (len as u64).max(1);2412        let coefficient: BalanceOf<T> = weight_saturation2413            .min(len_saturation)2414            .saturated_into::<BalanceOf<T>>();2415        final_fee2416            .saturating_mul(coefficient)2417            .saturated_into::<TransactionPriority>()2418    }24192420    fn withdraw_fee(2421        &self,2422        who: &T::AccountId,2423        call: &T::Call,2424        info: &DispatchInfoOf<T::Call>,2425        len: usize,2426	) -> Result<2427		(2428			BalanceOf<T>,2429			<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2430		),2431		TransactionValidityError,2432	> {2433        let tip = self.0;24342435        // Set fee based on call type. Creating collection costs 1 Unique.2436        // All other transactions have traditional fees so far2437        // let fee = match call.is_sub_type() {2438        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2439        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2440        //                                                 // _ => <BalanceOf<T>>::from(100)2441        // };2442        let fee = Self::traditional_fee(len, info, tip);24432444        // Only mess with balances if fee is not zero.2445        if fee.is_zero() {2446            return <<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2447			.map(|i| (fee, i));2448        }24492450        // Determine who is paying transaction fee based on ecnomic model2451        // Parse call to extract collection ID and access collection sponsor2452        let mut sponsor: T::AccountId = match IsSubType::<Call<T>>::is_sub_type(call) {2453            Some(Call::create_item(collection_id, _owner, _properties)) => {2454                let collection = <Collection<T>>::get(collection_id).unwrap();24552456                // sponsor timeout2457                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24582459                let limit = collection.limits.sponsor_transfer_timeout;2460                let mut sponsored = true;2461                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2462                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2463                    let limit_time = last_tx_block + limit.into();2464                    if block_number <= limit_time {2465                        sponsored = false;2466                    }2467                }2468                if sponsored {2469                    <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);2470                }24712472                // check free create limit2473                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2474                   (collection.sponsor_confirmed) &&2475                   (sponsored)2476                {2477                    collection.sponsor2478                } else {2479                    T::AccountId::default()2480                }2481            }2482            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2483                let collection = <Collection<T>>::get(collection_id).unwrap();2484                2485                let mut sponsor_transfer = false;2486                if collection.sponsor_confirmed {24872488                    let collection_limits = collection.limits;2489                    let collection_mode = collection.mode;2490    2491                    // sponsor timeout2492                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2493                    sponsor_transfer = match collection_mode {2494                        CollectionMode::NFT => {2495    2496                            // get correct limit2497                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2498                                collection_limits.sponsor_transfer_timeout2499                            } else {2500                                ChainLimit::get().nft_sponsor_transfer_timeout2501                            };2502    2503                            let mut sponsored = true;2504                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2505                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2506                                let limit_time = last_tx_block + limit.into();2507                                if block_number <= limit_time {2508                                    sponsored = false;2509                                }2510                            }2511                            if sponsored {2512                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2513                            }25142515                            sponsored2516                        }2517                        CollectionMode::Fungible(_) => {2518    2519                            // get correct limit2520                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2521                                collection_limits.sponsor_transfer_timeout2522                            } else {2523                                ChainLimit::get().fungible_sponsor_transfer_timeout2524                            };2525    2526                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2527                            let mut sponsored = true;2528                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2529                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2530                                let limit_time = last_tx_block + limit.into();2531                                if block_number <= limit_time {2532                                    sponsored = false;2533                                }2534                            }2535                            if sponsored {2536                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2537                            }25382539                            sponsored2540                        }2541                        CollectionMode::ReFungible => {2542    2543                            // get correct limit2544                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2545                                collection_limits.sponsor_transfer_timeout2546                            } else {2547                                ChainLimit::get().refungible_sponsor_transfer_timeout2548                            };2549    2550                            let mut sponsored = true;2551                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2552                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2553                                let limit_time = last_tx_block + limit.into();2554                                if block_number <= limit_time {2555                                    sponsored = false;2556                                }2557                            }2558                            if sponsored {2559                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2560                            }25612562                            sponsored2563                        }2564                        _ => {2565                            false2566                        },2567                    };2568                }25692570                if !sponsor_transfer {2571                    T::AccountId::default()2572                } else {2573                    collection.sponsor2574                }2575            }25762577            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2578                let mut sponsor_metadata_changes = false;25792580                let collection = <Collection<T>>::get(collection_id).unwrap();25812582                if2583                    collection.sponsor_confirmed &&2584                    // Can't sponsor fungible collection, this tx will be rejected2585                    // as invalid2586                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2587                    data.len() <= collection.limits.sponsored_data_size as usize2588                {2589                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2590                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;25912592                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2593                            .map(|last_block| block_number - last_block > rate_limit)2594                            .unwrap_or(true) 2595                        {2596                            sponsor_metadata_changes = true;2597                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2598                        }2599                    }2600                }26012602                if !sponsor_metadata_changes {2603                    T::AccountId::default()2604                } else {2605                    collection.sponsor2606                }2607            }26082609            _ => T::AccountId::default(),2610        };26112612        // Sponsor smart contracts2613        sponsor = match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26142615            // On instantiation: set the contract owner2616            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26172618                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2619                    &who,2620                    code_hash,2621                    salt,2622                );2623                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26242625                T::AccountId::default()2626            },26272628            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2629            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26302631                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26322633                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2634                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2635                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2636                  2637                if !owned_contract && white_list_enabled {2638                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2639                        return Err(InvalidTransaction::Call.into());2640                    }2641                }26422643                let mut sponsor_transfer = false;2644                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2645                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2646                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2647                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2648                    let limit_time = last_tx_block + rate_limit;26492650                    if block_number >= limit_time {2651                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2652                        sponsor_transfer = true;2653                    }2654                } else {2655                    sponsor_transfer = false;2656                }2657               2658                2659                let mut sp = T::AccountId::default();2660                if sponsor_transfer {2661                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2662                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2663                            sp = called_contract;2664                        }2665                    }2666                }26672668                sp2669            },26702671            _ => sponsor,2672        };26732674        let mut who_pays_fee: T::AccountId = sponsor.clone();2675        if sponsor == T::AccountId::default() {2676            who_pays_fee = who.clone();2677        }26782679		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2680			.map(|i| (fee, i))2681    }2682}268326842685impl<T: Config + Send + Sync> SignedExtension2686    for ChargeTransactionPayment<T>2687where2688    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2689    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2690    T::AccountId: AsRef<[u8]>,2691    T::AccountId: UncheckedFrom<T::Hash>,2692{2693    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2694    type AccountId = T::AccountId;2695    type Call = T::Call;2696    type AdditionalSigned = ();2697    type Pre = (2698        // tip2699        BalanceOf<T>,2700        // who pays fee2701        Self::AccountId,2702		// imbalance resulting from withdrawing the fee2703		<<T as transaction_payment::Config>::OnChargeTransaction as transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2704    );2705    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2706        Ok(())2707    }27082709    fn validate(2710        &self,2711        who: &Self::AccountId,2712        call: &Self::Call,2713        info: &DispatchInfoOf<Self::Call>,2714        len: usize,2715    ) -> TransactionValidity {2716		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2717		Ok(ValidTransaction {2718			priority: Self::get_priority(len, info, fee),2719			..Default::default()2720		})2721    }27222723    fn pre_dispatch(2724        self,2725        who: &Self::AccountId,2726        call: &Self::Call,2727        info: &DispatchInfoOf<Self::Call>,2728        len: usize,2729    ) -> Result<Self::Pre, TransactionValidityError> {2730        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2731        Ok((self.0, who.clone(), imbalance))2732    }27332734    fn post_dispatch(2735        pre: Self::Pre,2736        info: &DispatchInfoOf<Self::Call>,2737        post_info: &PostDispatchInfoOf<Self::Call>,2738        len: usize,2739        _result: &DispatchResult,2740    ) -> Result<(), TransactionValidityError> {2741		let (tip, who, imbalance) = pre;2742		let actual_fee = transaction_payment::Module::<T>::compute_actual_fee(2743			len as u32,2744			info,2745			post_info,2746			tip,2747		);2748		<T as transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2749		Ok(())2750    }2751}27522753// #endregion