git.delta.rocks / unique-network / refs/commits / 030d76fe47d1

difftreelog

Merge commit 'e12feb6d611d7bba7b295d94c8a6502e6a701286' into feature/NFTPAR-196_sponsor_setVariableMetadata

Yaroslav Bolyukin2021-03-09parents: #caec01d #e12feb6.patch.diff
in: master

1 file changed

modifiedpallets/nft/src/lib.rsdiffbeforeafterboth
before · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct Collection<T: Config> {129    pub id: CollectionId,130    pub owner: T::AccountId,131    pub mode: CollectionMode,132    pub access: AccessMode,133    pub decimal_points: DecimalPoints,134    pub name: Vec<u16>,        // 64 include null escape char135    pub description: Vec<u16>, // 256 include null escape char136    pub token_prefix: Vec<u8>, // 16 include null escape char137    pub mint_mode: bool,138    pub offchain_schema: Vec<u8>,139    pub schema_version: SchemaVersion,140    pub sponsor: T::AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender141    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.142    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 143    pub variable_on_chain_schema: Vec<u8>, //144    pub const_on_chain_schema: Vec<u8>, //145}146147#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]148#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]149pub struct NftItemType<AccountId> {150    pub owner: AccountId,151    pub const_data: Vec<u8>,152    pub variable_data: Vec<u8>,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct FungibleItemType {158    pub value: u128,159}160161#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]162#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]163pub struct ReFungibleItemType<AccountId> {164    pub owner: Vec<Ownership<AccountId>>,165    pub const_data: Vec<u8>,166    pub variable_data: Vec<u8>,167}168169// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]170// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]171// pub struct VestingItem<AccountId, Moment> {172//     pub sender: AccountId,173//     pub recipient: AccountId,174//     pub collection_id: CollectionId,175//     pub item_id: TokenId,176//     pub amount: u64,177//     pub vesting_date: Moment,178// }179180#[derive(Encode, Decode, Debug, Clone, PartialEq)]181#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]182pub struct CollectionLimits<BlockNumber: Encode + Decode> {183    pub account_token_ownership_limit: u32,184    pub sponsored_data_size: u32,185    /// None - setVariableMetadata is not sponsored186    /// Some(v) - setVariableMetadata is sponsored 187    ///           if there is v block between txs188    pub sponsored_data_rate_limit: Option<BlockNumber>,189    pub token_limit: u32,190191    // Timeouts for item types in passed blocks192    pub sponsor_transfer_timeout: u32,193    pub owner_can_transfer: bool,194    pub owner_can_destroy: bool,195}196197impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {198    fn default() -> Self {199        Self { 200            account_token_ownership_limit: 10_000_000, 201            token_limit: u32::max_value(),202            sponsored_data_size: u32::MAX, 203            sponsored_data_rate_limit: None,204            sponsor_transfer_timeout: 14400,205            owner_can_transfer: true,206            owner_can_destroy: true207        }208    }209}210211#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]212#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]213pub struct ChainLimits {214    pub collection_numbers_limit: u32,215    pub account_token_ownership_limit: u32,216    pub collections_admins_limit: u64,217    pub custom_data_limit: u32,218219    // Timeouts for item types in passed blocks220    pub nft_sponsor_transfer_timeout: u32,221    pub fungible_sponsor_transfer_timeout: u32,222    pub refungible_sponsor_transfer_timeout: u32,223224    // Schema limits225    pub offchain_schema_limit: u32,226    pub variable_on_chain_schema_limit: u32,227    pub const_on_chain_schema_limit: u32,228}229230pub trait WeightInfo {231	fn create_collection() -> Weight;232	fn destroy_collection() -> Weight;233	fn add_to_white_list() -> Weight;234	fn remove_from_white_list() -> Weight;235    fn set_public_access_mode() -> Weight;236    fn set_mint_permission() -> Weight;237    fn change_collection_owner() -> Weight;238    fn add_collection_admin() -> Weight;239    fn remove_collection_admin() -> Weight;240    fn set_collection_sponsor() -> Weight;241    fn confirm_sponsorship() -> Weight;242    fn remove_collection_sponsor() -> Weight;243    fn create_item(s: usize) -> Weight;244    fn burn_item() -> Weight;245    fn transfer() -> Weight;246    fn approve() -> Weight;247    fn transfer_from() -> Weight;248    fn set_offchain_schema() -> Weight;249    fn set_const_on_chain_schema() -> Weight;250    fn set_variable_on_chain_schema() -> Weight;251    fn set_variable_meta_data() -> Weight;252    fn enable_contract_sponsoring() -> Weight;253    fn set_schema_version() -> Weight;254    fn set_chain_limits() -> Weight;255    fn set_contract_sponsoring_rate_limit() -> Weight;256    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273    pub value: u128,274}275276#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CreateReFungibleData {279    pub const_data: Vec<u8>,280    pub variable_data: Vec<u8>,281    pub pieces: u128,282}283284#[derive(Encode, Decode, Debug, Clone, PartialEq)]285#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]286pub enum CreateItemData {287    NFT(CreateNftData),288    Fungible(CreateFungibleData),289    ReFungible(CreateReFungibleData),290}291292impl CreateItemData {293    pub fn len(&self) -> usize {294        let len = match self {295            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),296            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),297            _ => 0298        };299        300        return len;301    }302}303304impl From<CreateNftData> for CreateItemData {305    fn from(item: CreateNftData) -> Self {306        CreateItemData::NFT(item)307    }308}309310impl From<CreateReFungibleData> for CreateItemData {311    fn from(item: CreateReFungibleData) -> Self {312        CreateItemData::ReFungible(item)313    }314}315316impl From<CreateFungibleData> for CreateItemData {317    fn from(item: CreateFungibleData) -> Self {318        CreateItemData::Fungible(item)319    }320}321322323decl_error! {324	/// Error for non-fungible-token module.325	pub enum Error for Module<T: Config> {326        /// Total collections bound exceeded.327        TotalCollectionsLimitExceeded,328		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.329        CollectionDecimalPointLimitExceeded, 330        /// Collection name can not be longer than 63 char.331        CollectionNameLimitExceeded, 332        /// Collection description can not be longer than 255 char.333        CollectionDescriptionLimitExceeded, 334        /// Token prefix can not be longer than 15 char.335        CollectionTokenPrefixLimitExceeded,336        /// This collection does not exist.337        CollectionNotFound,338        /// Item not exists.339        TokenNotFound,340        /// Admin not found341        AdminNotFound,342        /// Arithmetic calculation overflow.343        NumOverflow,       344        /// Account already has admin role.345        AlreadyAdmin,  346        /// You do not own this collection.347        NoPermission,348        /// This address is not set as sponsor, use setCollectionSponsor first.349        ConfirmUnsetSponsorFail,350        /// Collection is not in mint mode.351        PublicMintingNotAllowed,352        /// Sender parameter and item owner must be equal.353        MustBeTokenOwner,354        /// Item balance not enough.355        TokenValueTooLow,356        /// Size of item is too large.357        NftSizeLimitExceeded,358        /// No approve found359        ApproveNotFound,360        /// Requested value more than approved.361        TokenValueNotEnough,362        /// Only approved addresses can call this method.363        ApproveRequired,364        /// Address is not in white list.365        AddresNotInWhiteList,366        /// Number of collection admins bound exceeded.367        CollectionAdminsLimitExceeded,368        /// Owned tokens by a single address bound exceeded.369        AddressOwnershipLimitExceeded,370        /// Length of items properties must be greater than 0.371        EmptyArgument,372        /// const_data exceeded data limit.373        TokenConstDataLimitExceeded,374        /// variable_data exceeded data limit.375        TokenVariableDataLimitExceeded,376        /// Not NFT item data used to mint in NFT collection.377        NotNftDataUsedToMintNftCollectionToken,378        /// Not Fungible item data used to mint in Fungible collection.379        NotFungibleDataUsedToMintFungibleCollectionToken,380        /// Not Re Fungible item data used to mint in Re Fungible collection.381        NotReFungibleDataUsedToMintReFungibleCollectionToken,382        /// Unexpected collection type.383        UnexpectedCollectionType,384        /// Can't store metadata in fungible tokens.385        CantStoreMetadataInFungibleTokens,386        /// Collection token limit exceeded387        CollectionTokenLimitExceeded,388        /// Account token limit exceeded per collection389        AccountTokenLimitExceeded,390        /// Collection limit bounds per collection exceeded391        CollectionLimitBoundsExceeded,392        /// Tried to enable permissions which are only permitted to be disabled393        OwnerPermissionsCantBeReverted,394        /// Schema data size limit bound exceeded395        SchemaDataLimitExceeded,396        /// Maximum refungibility exceeded397        WrongRefungiblePieces398	}399}400401pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {402    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;403404    /// Weight information for extrinsics in this pallet.405	type WeightInfo: WeightInfo;406}407408#[cfg(feature = "runtime-benchmarks")]409mod benchmarking;410411// #endregion412413decl_storage! {414    trait Store for Module<T: Config> as Nft {415416        // Private members417        NextCollectionID: CollectionId;418        CreatedCollectionCount: u32;419        ChainVersion: u64;420        ItemListIndex: map hasher(identity) CollectionId => TokenId;421422        // Chain limits struct423        pub ChainLimit get(fn chain_limit) config(): ChainLimits;424425        // Bound counters426        CollectionCount: u32;427        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;428429        // Basic collections430        pub CollectionById get(fn collection_id) config(): map hasher(identity) CollectionId => Option<Collection<T>> = None;431        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;432        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;433434        /// Balance owner per collection map435        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;436437        /// second parameter: item id + owner account id + spender account id438        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;439440        /// Item collections441        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;442        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;443        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;444445        /// Index list446        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;447448        /// Tokens transfer baskets449        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;450        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;451        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;453454        /// Variable metadata sponsoring455        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;456457        // Contract Sponsorship and Ownership458        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;459        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;460        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;461        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;462        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 463        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 464    }465    add_extra_genesis {466        build(|config: &GenesisConfig<T>| {467            // Modification of storage468            for (_num, _c) in &config.collection_id {469                <Module<T>>::init_collection(_c);470            }471472            for (_num, _c, _i) in &config.nft_item_id {473                <Module<T>>::init_nft_token(*_c, _i);474            }475476            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {477                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);478            }479480            for (_num, _c, _i) in &config.refungible_item_id {481                <Module<T>>::init_refungible_token(*_c, _i);482            }483        })484    }485}486487decl_event!(488    pub enum Event<T>489    where490        AccountId = <T as system::Config>::AccountId,491    {492        /// New collection was created493        /// 494        /// # Arguments495        /// 496        /// * collection_id: Globally unique identifier of newly created collection.497        /// 498        /// * mode: [CollectionMode] converted into u8.499        /// 500        /// * account_id: Collection owner.501        Created(CollectionId, u8, AccountId),502503        /// New item was created.504        /// 505        /// # Arguments506        /// 507        /// * collection_id: Id of the collection where item was created.508        /// 509        /// * item_id: Id of an item. Unique within the collection.510        ///511        /// * recipient: Owner of newly created item 512        ItemCreated(CollectionId, TokenId, AccountId),513514        /// Collection item was burned.515        /// 516        /// # Arguments517        /// 518        /// collection_id.519        /// 520        /// item_id: Identifier of burned NFT.521        ItemDestroyed(CollectionId, TokenId),522523        /// Item was transferred524        ///525        /// * collection_id: Id of collection to which item is belong526        ///527        /// * item_id: Id of an item528        ///529        /// * sender: Original owner of item530        ///531        /// * recipient: New owner of item532        ///533        /// * amount: Always 1 for NFT534        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),535    }536);537538decl_module! {539    pub struct Module<T: Config> for enum Call 540    where 541        origin: T::Origin542    {543        fn deposit_event() = default;544        type Error = Error<T>;545546        fn on_initialize(now: T::BlockNumber) -> Weight {547548            if ChainVersion::get() < 2549            {550                let value = NextCollectionID::get();551                CreatedCollectionCount::put(value);552                ChainVersion::put(2);553            }554555            0556        }557558        /// 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.559        /// 560        /// # Permissions561        /// 562        /// * Anyone.563        /// 564        /// # Arguments565        /// 566        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.567        /// 568        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.569        /// 570        /// * token_prefix: UTF-8 string with token prefix.571        /// 572        /// * mode: [CollectionMode] collection type and type dependent data.573        // returns collection ID574        #[weight = <T as Config>::WeightInfo::create_collection()]575        pub fn create_collection(origin,576                                 collection_name: Vec<u16>,577                                 collection_description: Vec<u16>,578                                 token_prefix: Vec<u8>,579                                 mode: CollectionMode) -> DispatchResult {580581            // Anyone can create a collection582            let who = ensure_signed(origin)?;583584            let decimal_points = match mode {585                CollectionMode::Fungible(points) => points,586                _ => 0587            };588589            let chain_limit = ChainLimit::get();590591            // bound Total number of collections592            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);593594            // check params595            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);596            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);597            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);598            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);599600            // Generate next collection ID601            let next_id = CreatedCollectionCount::get()602                .checked_add(1)603                .ok_or(Error::<T>::NumOverflow)?;604605            // bound counter606            let total = CollectionCount::get()607                .checked_add(1)608                .ok_or(Error::<T>::NumOverflow)?;609610            CreatedCollectionCount::put(next_id);611            CollectionCount::put(total);612613            let limits = CollectionLimits {614                sponsored_data_size: chain_limit.custom_data_limit,615                ..Default::default()616            };617618            // Create new collection619            let new_collection = Collection {620                id: next_id,621                owner: who.clone(),622                name: collection_name,623                mode: mode.clone(),624                mint_mode: false,625                access: AccessMode::Normal,626                description: collection_description,627                decimal_points: decimal_points,628                token_prefix: token_prefix,629                offchain_schema: Vec::new(),630                schema_version: SchemaVersion::ImageURL,631                sponsor: T::AccountId::default(),632                sponsor_confirmed: false,633                variable_on_chain_schema: Vec::new(),634                const_on_chain_schema: Vec::new(),635                limits,636            };637638            // Add new collection to map639            <CollectionById<T>>::insert(next_id, new_collection);640641            // call event642            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));643644            Ok(())645        }646647        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.648        /// 649        /// # Permissions650        /// 651        /// * Collection Owner.652        /// 653        /// # Arguments654        /// 655        /// * collection_id: collection to destroy.656        #[weight = <T as Config>::WeightInfo::destroy_collection()]657        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {658659            let sender = ensure_signed(origin)?;660            let collection = Self::get_collection(collection_id)?;661            Self::check_owner_permissions(&collection, sender)?;662            if !collection.limits.owner_can_destroy {663                fail!(Error::<T>::NoPermission);664            }665666            <AddressTokens<T>>::remove_prefix(collection_id);667            <Allowances<T>>::remove_prefix(collection_id);668            <Balance<T>>::remove_prefix(collection_id);669            <ItemListIndex>::remove(collection_id);670            <AdminList<T>>::remove(collection_id);671            <CollectionById<T>>::remove(collection_id);672            <WhiteList<T>>::remove_prefix(collection_id);673674            <NftItemList<T>>::remove_prefix(collection_id);675            <FungibleItemList<T>>::remove_prefix(collection_id);676            <ReFungibleItemList<T>>::remove_prefix(collection_id);677678            <NftTransferBasket<T>>::remove_prefix(collection_id);679            <FungibleTransferBasket<T>>::remove_prefix(collection_id);680            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);681682            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);683684            if CollectionCount::get() > 0685            {686                // bound couter687                let total = CollectionCount::get()688                    .checked_sub(1)689                    .ok_or(Error::<T>::NumOverflow)?;690691                CollectionCount::put(total);692            }693694            Ok(())695        }696697        /// Add an address to white list.698        /// 699        /// # Permissions700        /// 701        /// * Collection Owner702        /// * Collection Admin703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * address.709        #[weight = <T as Config>::WeightInfo::add_to_white_list()]710        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{711712            let sender = ensure_signed(origin)?;713            let collection = Self::get_collection(collection_id)?;714            Self::check_owner_or_admin_permissions(&collection, sender)?;715716            <WhiteList<T>>::insert(collection_id, address, true);717            718            Ok(())719        }720721        /// Remove an address from white list.722        /// 723        /// # Permissions724        /// 725        /// * Collection Owner726        /// * Collection Admin727        /// 728        /// # Arguments729        /// 730        /// * collection_id.731        /// 732        /// * address.733        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]734        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{735736            let sender = ensure_signed(origin)?;737            let collection = Self::get_collection(collection_id)?;738            Self::check_owner_or_admin_permissions(&collection, sender)?;739740            <WhiteList<T>>::remove(collection_id, address);741742            Ok(())743        }744745        /// Toggle between normal and white list access for the methods with access for `Anyone`.746        /// 747        /// # Permissions748        /// 749        /// * Collection Owner.750        /// 751        /// # Arguments752        /// 753        /// * collection_id.754        /// 755        /// * mode: [AccessMode]756        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]757        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult758        {759            let sender = ensure_signed(origin)?;760761            let mut target_collection = Self::get_collection(collection_id)?;762            Self::check_owner_permissions(&target_collection, sender)?;763            target_collection.access = mode;764            Self::save_collection(target_collection);765766            Ok(())767        }768769        /// Allows Anyone to create tokens if:770        /// * White List is enabled, and771        /// * Address is added to white list, and772        /// * This method was called with True parameter773        /// 774        /// # Permissions775        /// * Collection Owner776        ///777        /// # Arguments778        /// 779        /// * collection_id.780        /// 781        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.782        #[weight = <T as Config>::WeightInfo::set_mint_permission()]783        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult784        {785            let sender = ensure_signed(origin)?;786787            let mut target_collection = Self::get_collection(collection_id)?;788            Self::check_owner_permissions(&target_collection, sender)?;789            target_collection.mint_mode = mint_permission;790            Self::save_collection(target_collection);791792            Ok(())793        }794795        /// Change the owner of the collection.796        /// 797        /// # Permissions798        /// 799        /// * Collection Owner.800        /// 801        /// # Arguments802        /// 803        /// * collection_id.804        /// 805        /// * new_owner.806        #[weight = <T as Config>::WeightInfo::change_collection_owner()]807        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {808809            let sender = ensure_signed(origin)?;810            let mut target_collection = Self::get_collection(collection_id)?;811            Self::check_owner_permissions(&target_collection, sender)?;812            target_collection.owner = new_owner;813            Self::save_collection(target_collection);814815            Ok(())816        }817818        /// Adds an admin of the Collection.819        /// 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. 820        /// 821        /// # Permissions822        /// 823        /// * Collection Owner.824        /// * Collection Admin.825        /// 826        /// # Arguments827        /// 828        /// * collection_id: ID of the Collection to add admin for.829        /// 830        /// * new_admin_id: Address of new admin to add.831        #[weight = <T as Config>::WeightInfo::add_collection_admin()]832        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {833834            let sender = ensure_signed(origin)?;835            let collection = Self::get_collection(collection_id)?;836            Self::check_owner_or_admin_permissions(&collection, sender)?;837            let mut admin_arr: Vec<T::AccountId> = Vec::new();838839            if <AdminList<T>>::contains_key(collection_id)840            {841                admin_arr = <AdminList<T>>::get(collection_id);842                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);843            }844845            // Number of collection admins846            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);847848            admin_arr.push(new_admin_id);849            <AdminList<T>>::insert(collection_id, admin_arr);850851            Ok(())852        }853854        /// 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.855        ///856        /// # Permissions857        /// 858        /// * Collection Owner.859        /// * Collection Admin.860        /// 861        /// # Arguments862        /// 863        /// * collection_id: ID of the Collection to remove admin for.864        /// 865        /// * account_id: Address of admin to remove.866        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]867        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            let collection = Self::get_collection(collection_id)?;871            Self::check_owner_or_admin_permissions(&collection, sender)?;872            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);873874            let mut admin_arr = <AdminList<T>>::get(collection_id);875            admin_arr.retain(|i| *i != account_id);876            <AdminList<T>>::insert(collection_id, admin_arr);877878            Ok(())879        }880881        /// # Permissions882        /// 883        /// * Collection Owner884        /// 885        /// # Arguments886        /// 887        /// * collection_id.888        /// 889        /// * new_sponsor.890        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]891        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {892893            let sender = ensure_signed(origin)?;894            let mut target_collection = Self::get_collection(collection_id)?;895            Self::check_owner_permissions(&target_collection, sender)?;896897            target_collection.sponsor = new_sponsor;898            target_collection.sponsor_confirmed = false;899            Self::save_collection(target_collection);900901            Ok(())902        }903904        /// # Permissions905        /// 906        /// * Sponsor.907        /// 908        /// # Arguments909        /// 910        /// * collection_id.911        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]912        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {913914            let sender = ensure_signed(origin)?;915916            let mut target_collection = Self::get_collection(collection_id)?;917            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);918919            target_collection.sponsor_confirmed = true;920            Self::save_collection(target_collection);921922            Ok(())923        }924925        /// Switch back to pay-per-own-transaction model.926        ///927        /// # Permissions928        ///929        /// * Collection owner.930        /// 931        /// # Arguments932        /// 933        /// * collection_id.934        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]935        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {936937            let sender = ensure_signed(origin)?;938939            let mut target_collection = Self::get_collection(collection_id)?;940            Self::check_owner_permissions(&target_collection, sender)?;941942            target_collection.sponsor = T::AccountId::default();943            target_collection.sponsor_confirmed = false;944            Self::save_collection(target_collection);945946            Ok(())947        }948949        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.950        /// 951        /// # Permissions952        /// 953        /// * Collection Owner.954        /// * Collection Admin.955        /// * Anyone if956        ///     * White List is enabled, and957        ///     * Address is added to white list, and958        ///     * MintPermission is enabled (see SetMintPermission method)959        /// 960        /// # Arguments961        /// 962        /// * collection_id: ID of the collection.963        /// 964        /// * owner: Address, initial owner of the NFT.965        ///966        /// * data: Token data to store on chain.967        // #[weight =968        // (130_000_000 as Weight)969        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))970        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))971        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]972973        #[weight = <T as Config>::WeightInfo::create_item(data.len())]974        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {975976            let sender = ensure_signed(origin)?;977978            let target_collection = Self::get_collection(collection_id)?;979980            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;981            Self::validate_create_item_args(&target_collection, &data)?;982            Self::create_item_no_validation(&target_collection, owner, data)?;983984            Ok(())985        }986987        /// This method creates multiple instances of NFT Collection created with CreateCollection method.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// * Anyone if994        ///     * White List is enabled, and995        ///     * Address is added to white list, and996        ///     * MintPermission is enabled (see SetMintPermission method)997        /// 998        /// # Arguments999        /// 1000        /// * collection_id: ID of the collection.1001        /// 1002        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1003        /// 1004        /// * owner: Address, initial owner of the NFT.1005        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1006                               .map(|data| { data.len() })1007                               .sum())]1008        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10091010            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1011            let sender = ensure_signed(origin)?;10121013            let target_collection = Self::get_collection(collection_id)?;10141015            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;10161017            for data in &items_data {1018                Self::validate_create_item_args(&target_collection, data)?;1019            }1020            for data in &items_data {1021                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1022            }10231024            Ok(())1025        }10261027        /// Destroys a concrete instance of NFT.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner.1032        /// * Collection Admin.1033        /// * Current NFT Owner.1034        /// 1035        /// # Arguments1036        /// 1037        /// * collection_id: ID of the collection.1038        /// 1039        /// * item_id: ID of NFT to burn.1040        #[weight = <T as Config>::WeightInfo::burn_item()]1041        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10421043            let sender = ensure_signed(origin)?;10441045            // Transfer permissions check1046            let target_collection = Self::get_collection(collection_id)?;1047            ensure!(1048                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1049                (1050                    target_collection.limits.owner_can_transfer &&1051                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1052                ),1053                Error::<T>::NoPermission1054            );10551056            if target_collection.access == AccessMode::WhiteList {1057                Self::check_white_list(&target_collection, &sender)?;1058            }10591060            match target_collection.mode1061            {1062                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1063                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,1064                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1065                _ => ()1066            };10671068            // call event1069            Self::deposit_event(RawEvent::ItemDestroyed(target_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            let collection = Self::get_collection(collection_id)?;11011102            Self::transfer_internal(sender, recipient, &collection, item_id, value)1103        }11041105        /// Set, change, or remove approved address to transfer the ownership of the NFT.1106        /// 1107        /// # Permissions1108        /// 1109        /// * Collection Owner1110        /// * Collection Admin1111        /// * Current NFT owner1112        /// 1113        /// # Arguments1114        /// 1115        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1116        /// 1117        /// * collection_id.1118        /// 1119        /// * item_id: ID of the item.1120        #[weight = <T as Config>::WeightInfo::approve()]1121        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11221123            let sender = ensure_signed(origin)?;1124            let target_collection = Self::get_collection(collection_id)?;11251126            Self::token_exists(&target_collection, item_id, &sender)?;11271128            // Transfer permissions check1129            let allowance_limit = if (1130                target_collection.limits.owner_can_transfer &&1131                Self::is_owner_or_admin_permissions(1132                    &target_collection,1133                    sender.clone(),1134                ) 1135            ) {1136                None1137            } else if let Some(amount) = Self::owned_amount(1138                sender.clone(),1139                &target_collection,1140                item_id,1141            ) {1142                Some(amount)1143            } else {1144                fail!(Error::<T>::NoPermission);1145            };11461147            if target_collection.access == AccessMode::WhiteList {1148                Self::check_white_list(&target_collection, &sender)?;1149                Self::check_white_list(&target_collection, &spender)?;1150            }11511152            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1153            let mut allowance: u128 = amount;1154            if allowance_exists {1155                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1156            }1157            if let Some(limit) = allowance_limit {1158                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1159            }1160            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11611162            Ok(())1163        }1164        1165        /// 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.1166        /// 1167        /// # Permissions1168        /// * Collection Owner1169        /// * Collection Admin1170        /// * Current NFT owner1171        /// * Address approved by current NFT owner1172        /// 1173        /// # Arguments1174        /// 1175        /// * from: Address that owns token.1176        /// 1177        /// * recipient: Address of token recipient.1178        /// 1179        /// * collection_id.1180        /// 1181        /// * item_id: ID of the item.1182        /// 1183        /// * value: Amount to transfer.1184        #[weight = <T as Config>::WeightInfo::transfer_from()]1185        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11861187            let sender = ensure_signed(origin)?;1188            let target_collection = Self::get_collection(collection_id)?;11891190            let mut appoved_transfer = false;11911192            // Check approval1193            let mut approval: u128 = 0;1194            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1195                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1196                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1197                appoved_transfer = true;1198            }11991200            // Limits check1201            Self::is_correct_transfer(&target_collection, &recipient)?;12021203            // Transfer permissions check         1204            ensure!(1205                appoved_transfer || 1206                (1207                    target_collection.limits.owner_can_transfer &&1208                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1209                ),1210                Error::<T>::NoPermission1211            );12121213            if target_collection.access == AccessMode::WhiteList {1214                Self::check_white_list(&target_collection, &sender)?;1215                Self::check_white_list(&target_collection, &recipient)?;1216            }12171218            // Reduce approval by transferred amount or remove if remaining approval drops to 01219            if approval.checked_sub(value).unwrap_or(0) > 0 {1220                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1221            }1222            else {1223                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1224            }12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from, recipient)?,1229                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1230                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient)?,1231                _ => ()1232            };12331234            Ok(())1235        }12361237        // #[weight = 0]1238        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12391240        //     // let no_perm_mes = "You do not have permissions to modify this collection";1241        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1242        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1243        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12441245        //     // // on_nft_received  call12461247        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12481249        //     Ok(())1250        // }12511252        /// Set off-chain data schema.1253        /// 1254        /// # Permissions1255        /// 1256        /// * Collection Owner1257        /// * Collection Admin1258        /// 1259        /// # Arguments1260        /// 1261        /// * collection_id.1262        /// 1263        /// * schema: String representing the offchain data schema.1264        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1265        pub fn set_variable_meta_data (1266            origin,1267            collection_id: CollectionId,1268            item_id: TokenId,1269            data: Vec<u8>1270        ) -> DispatchResult {1271            let sender = ensure_signed(origin)?;1272            1273            let target_collection = Self::get_collection(collection_id)?;1274            Self::token_exists(&target_collection, item_id, &sender)?;12751276            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12771278            // Modify permissions check1279            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1280                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1281                Error::<T>::NoPermission);12821283            match target_collection.mode1284            {1285                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1286                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, 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            let mut target_collection = Self::get_collection(collection_id)?;1316            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1317            target_collection.schema_version = version;1318            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1343            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13441345            // check schema limit1346            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13471348            target_collection.offchain_schema = schema;1349            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1374            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13751376            // check schema limit1377            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13781379            target_collection.const_on_chain_schema = schema;1380            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1405            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14061407            // check schema limit1408            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14091410            target_collection.variable_on_chain_schema = schema;1411            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1581            Self::check_owner_permissions(&target_collection, sender.clone())?;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            Self::save_collection(target_collection);16031604            Ok(())1605        } 1606    }1607}16081609impl<T: Config> Module<T> {16101611    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &Collection<T>, item_id: TokenId, value: u128) -> DispatchResult {1612        // Limits check1613        Self::is_correct_transfer(target_collection, &recipient)?;16141615        // Transfer permissions check1616        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1617            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1618            Error::<T>::NoPermission);16191620        if target_collection.access == AccessMode::WhiteList {1621            Self::check_white_list(target_collection, &sender)?;1622            Self::check_white_list(target_collection, &recipient)?;1623        }16241625        match target_collection.mode1626        {1627            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1628            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1629            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1630            _ => ()1631        };16321633        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));16341635        Ok(())1636    }163716381639    fn is_correct_transfer(collection: &Collection<T>, recipient: &T::AccountId) -> DispatchResult {1640        let collection_id = collection.id;16411642        // check token limit and account token limit1643        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1644        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1645        1646        Ok(())1647    }16481649    fn can_create_items_in_collection(collection: &Collection<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {1650        let collection_id = collection.id;16511652        // check token limit and account token limit1653        let total_items: u32 = ItemListIndex::get(collection_id);1654        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1655        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1656        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16571658        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1659            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1660            Self::check_white_list(collection, owner)?;1661            Self::check_white_list(collection, sender)?;1662        }16631664        Ok(())1665    }16661667    fn validate_create_item_args(target_collection: &Collection<T>, data: &CreateItemData) -> DispatchResult {1668        match target_collection.mode1669        {1670            CollectionMode::NFT => {1671                if let CreateItemData::NFT(data) = data {1672                    // check sizes1673                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1674                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1675                } else {1676                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1677                }1678            },1679            CollectionMode::Fungible(_) => {1680                if let CreateItemData::Fungible(_) = data {1681                } else {1682                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1683                }1684            },1685            CollectionMode::ReFungible => {1686                if let CreateItemData::ReFungible(data) = data {16871688                    // check sizes1689                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1690                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16911692                    // Check refungibility limits1693                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1694                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1695                } else {1696                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1697                }1698            },1699            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1700        };17011702        Ok(())1703    }17041705    fn create_item_no_validation(collection: &Collection<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1706        let collection_id = collection.id;17071708        match data1709        {1710            CreateItemData::NFT(data) => {1711                let item = NftItemType {1712                    owner: owner.clone(),1713                    const_data: data.const_data,1714                    variable_data: data.variable_data1715                };17161717                Self::add_nft_item(collection, item)?;1718            },1719            CreateItemData::Fungible(data) => {1720                Self::add_fungible_item(collection, &owner, data.value)?;1721            },1722            CreateItemData::ReFungible(data) => {1723                let mut owner_list = Vec::new();1724                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17251726                let item = ReFungibleItemType {1727                    owner: owner_list,1728                    const_data: data.const_data,1729                    variable_data: data.variable_data1730                };17311732                Self::add_refungible_item(collection, item)?;1733            }1734        };17351736        // call event1737        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17381739        Ok(())1740    }17411742    fn add_fungible_item(collection: &Collection<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1743        let collection_id = collection.id;17441745        // Does new owner already have an account?1746        let mut balance: u128 = 0;1747        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1748            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1749        } 17501751        // Mint 1752        let item = FungibleItemType {1753            value: balance + value1754        };1755        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17561757        // Update balance1758        let new_balance = <Balance<T>>::get(collection_id, owner)1759            .checked_add(value)1760            .ok_or(Error::<T>::NumOverflow)?;1761        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17621763        Ok(())1764    }17651766    fn add_refungible_item(collection: &Collection<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1767        let collection_id = collection.id;17681769        let current_index = <ItemListIndex>::get(collection_id)1770            .checked_add(1)1771            .ok_or(Error::<T>::NumOverflow)?;1772        let itemcopy = item.clone();17731774        let value = item.owner.first().unwrap().fraction;1775        let owner = item.owner.first().unwrap().owner.clone();17761777        Self::add_token_index(collection_id, current_index, &owner)?;17781779        <ItemListIndex>::insert(collection_id, current_index);1780        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17811782        // Update balance1783        let new_balance = <Balance<T>>::get(collection_id, &owner)1784            .checked_add(value)1785            .ok_or(Error::<T>::NumOverflow)?;1786        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17871788        Ok(())1789    }17901791    fn add_nft_item(collection: &Collection<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1792        let collection_id = collection.id;17931794        let current_index = <ItemListIndex>::get(collection_id)1795            .checked_add(1)1796            .ok_or(Error::<T>::NumOverflow)?;17971798        let item_owner = item.owner.clone();1799        Self::add_token_index(collection_id, current_index, &item.owner)?;18001801        <ItemListIndex>::insert(collection_id, current_index);1802        <NftItemList<T>>::insert(collection_id, current_index, item);18031804        // Update balance1805        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1806            .checked_add(1)1807            .ok_or(Error::<T>::NumOverflow)?;1808        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18091810        Ok(())1811    }18121813    fn burn_refungible_item(1814        collection: &Collection<T>,1815        item_id: TokenId,1816        owner: &T::AccountId,1817    ) -> DispatchResult {1818        let collection_id = collection.id;18191820        ensure!(1821            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1822            Error::<T>::TokenNotFound1823        );1824        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1825        let rft_balance = token1826            .owner1827            .iter()1828            .filter(|&i| i.owner == *owner)1829            .next()1830            .unwrap();1831        Self::remove_token_index(collection_id, item_id, owner)?;18321833        // update balance1834        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1835            .checked_sub(rft_balance.fraction)1836            .ok_or(Error::<T>::NumOverflow)?;1837        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18381839        // Re-create owners list with sender removed1840        let index = token1841            .owner1842            .iter()1843            .position(|i| i.owner == *owner)1844            .unwrap();1845        token.owner.remove(index);1846        let owner_count = token.owner.len();18471848        // Burn the token completely if this was the last (only) owner1849        if owner_count == 0 {1850            <ReFungibleItemList<T>>::remove(collection_id, item_id);1851            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1852        }1853        else {1854            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1855        }18561857        Ok(())1858    }18591860    fn burn_nft_item(collection: &Collection<T>, item_id: TokenId) -> DispatchResult {1861        let collection_id = collection.id;18621863        ensure!(1864            <NftItemList<T>>::contains_key(collection_id, item_id),1865            Error::<T>::TokenNotFound1866        );1867        let item = <NftItemList<T>>::get(collection_id, item_id);1868        Self::remove_token_index(collection_id, item_id, &item.owner)?;18691870        // update balance1871        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1872            .checked_sub(1)1873            .ok_or(Error::<T>::NumOverflow)?;1874        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1875        <NftItemList<T>>::remove(collection_id, item_id);1876        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18771878        Ok(())1879    }18801881    fn burn_fungible_item(owner: &T::AccountId, collection: &Collection<T>, value: u128) -> DispatchResult {1882        let collection_id = collection.id;18831884        ensure!(1885            <FungibleItemList<T>>::contains_key(collection_id, owner),1886            Error::<T>::TokenNotFound1887        );1888        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1889        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18901891        // update balance1892        let new_balance = <Balance<T>>::get(collection_id, owner)1893            .checked_sub(value)1894            .ok_or(Error::<T>::NumOverflow)?;1895        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18961897        if balance.value - value > 0 {1898            balance.value -= value;1899            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900        }1901        else {1902            <FungibleItemList<T>>::remove(collection_id, owner);1903        }19041905        Ok(())1906    }19071908    pub fn get_collection(collection_id: CollectionId) -> Result<Collection<T>, sp_runtime::DispatchError> {1909        Ok(<CollectionById<T>>::get(collection_id)1910            .ok_or(Error::<T>::CollectionNotFound)?)1911    }19121913    fn save_collection(collection: Collection<T>) {1914        <CollectionById<T>>::insert(collection.id, collection);1915    }19161917    fn check_owner_permissions(target_collection: &Collection<T>, subject: T::AccountId) -> DispatchResult {1918        ensure!(1919            subject == target_collection.owner,1920            Error::<T>::NoPermission1921        );19221923        Ok(())1924    }19251926    fn is_owner_or_admin_permissions(collection: &Collection<T>, subject: T::AccountId) -> bool {1927        let mut result: bool = subject == collection.owner;1928        let exists = <AdminList<T>>::contains_key(collection.id);19291930        if !result & exists {1931            if <AdminList<T>>::get(collection.id).contains(&subject) {1932                result = true1933            }1934        }19351936        result1937    }19381939    fn check_owner_or_admin_permissions(1940        collection: &Collection<T>,1941        subject: T::AccountId,1942    ) -> DispatchResult {1943        let result = Self::is_owner_or_admin_permissions(collection, subject.clone());19441945        ensure!(1946            result,1947            Error::<T>::NoPermission1948        );1949        Ok(())1950    }19511952    fn owned_amount(1953        subject: T::AccountId,1954        target_collection: &Collection<T>,1955        item_id: TokenId,1956    ) -> Option<u128> {1957        let collection_id = target_collection.id;19581959        match target_collection.mode {1960            CollectionMode::NFT => {1961                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1962                    return Some(1)1963                }1964                None1965            },1966            CollectionMode::Fungible(_) => {1967                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1968                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1969                        .value);1970                }1971                None1972            },1973            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1974                .owner1975                .iter()1976                .find(|i| i.owner == subject)1977                .map(|i| i.fraction),1978            CollectionMode::Invalid => None,1979        }1980    }19811982    fn is_item_owner(subject: T::AccountId, target_collection: &Collection<T>, item_id: TokenId) -> bool {1983        let collection_id = target_collection.id;19841985        match target_collection.mode {1986            CollectionMode::NFT => {1987                <NftItemList<T>>::get(collection_id, item_id).owner == subject1988            }1989            CollectionMode::Fungible(_) => {1990                <FungibleItemList<T>>::contains_key(collection_id, &subject)1991            }1992            CollectionMode::ReFungible => {1993                <ReFungibleItemList<T>>::get(collection_id, item_id)1994                    .owner1995                    .iter()1996                    .any(|i| i.owner == subject)1997            }1998            CollectionMode::Invalid => false,1999        }2000    }20012002    fn check_white_list(collection: &Collection<T>, address: &T::AccountId) -> DispatchResult {2003        let collection_id = collection.id;20042005        let mes = Error::<T>::AddresNotInWhiteList;2006        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20072008        Ok(())2009    }20102011    /// Check if token exists. In case of Fungible, check if there is an entry for 2012    /// the owner in fungible balances double map2013    fn token_exists(2014        target_collection: &Collection<T>,2015        item_id: TokenId,2016        owner: &T::AccountId2017    ) -> DispatchResult {2018        let collection_id = target_collection.id;2019        let exists = match target_collection.mode2020        {2021            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2022            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2023            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2024            _ => false2025        };20262027        ensure!(exists == true, Error::<T>::TokenNotFound);2028        Ok(())2029    }20302031    fn transfer_fungible(2032        collection: &Collection<T>,2033        value: u128,2034        owner: &T::AccountId,2035        recipient: &T::AccountId,2036    ) -> DispatchResult {2037        let collection_id = collection.id;2038        Self::token_exists(&collection, 0, owner)?;20392040        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2041        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20422043        // Send balance to recipient (updates balanceOf of recipient)2044        Self::add_fungible_item(collection, recipient, value)?;20452046        // update balanceOf of sender2047        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20482049        // Reduce or remove sender2050        if balance.value == value {2051            <FungibleItemList<T>>::remove(collection_id, owner);2052        }2053        else {2054            balance.value -= value;2055            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2056        }20572058        Ok(())2059    }20602061    fn transfer_refungible(2062        collection: &Collection<T>,2063        item_id: TokenId,2064        value: u128,2065        owner: T::AccountId,2066        new_owner: T::AccountId,2067    ) -> DispatchResult {2068        let collection_id = collection.id;2069        Self::token_exists(collection, item_id, &owner)?;20702071        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2072        let item = full_item2073            .owner2074            .iter()2075            .filter(|i| i.owner == owner)2076            .next()2077            .ok_or(Error::<T>::NumOverflow)?;2078        let amount = item.fraction;20792080        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20812082        // update balance2083        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2084            .checked_sub(value)2085            .ok_or(Error::<T>::NumOverflow)?;2086        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20872088        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2089            .checked_add(value)2090            .ok_or(Error::<T>::NumOverflow)?;2091        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20922093        let old_owner = item.owner.clone();2094        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20952096        // transfer2097        if amount == value && !new_owner_has_account {2098            // change owner2099            // new owner do not have account2100            let mut new_full_item = full_item.clone();2101            new_full_item2102                .owner2103                .iter_mut()2104                .find(|i| i.owner == owner)2105                .unwrap()2106                .owner = new_owner.clone();2107            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21082109            // update index collection2110            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2111        } else {2112            let mut new_full_item = full_item.clone();2113            new_full_item2114                .owner2115                .iter_mut()2116                .find(|i| i.owner == owner)2117                .unwrap()2118                .fraction -= value;21192120            // separate amount2121            if new_owner_has_account {2122                // new owner has account2123                new_full_item2124                    .owner2125                    .iter_mut()2126                    .find(|i| i.owner == new_owner)2127                    .unwrap()2128                    .fraction += value;2129            } else {2130                // new owner do not have account2131                new_full_item.owner.push(Ownership {2132                    owner: new_owner.clone(),2133                    fraction: value,2134                });2135                Self::add_token_index(collection_id, item_id, &new_owner)?;2136            }21372138            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2139        }21402141        Ok(())2142    }21432144    fn transfer_nft(2145        collection: &Collection<T>,2146        item_id: TokenId,2147        sender: T::AccountId,2148        new_owner: T::AccountId,2149    ) -> DispatchResult {2150        let collection_id = collection.id;2151        Self::token_exists(&collection, item_id, &sender)?;21522153        let mut item = <NftItemList<T>>::get(collection_id, item_id);21542155        ensure!(2156            sender == item.owner,2157            Error::<T>::MustBeTokenOwner2158        );21592160        // update balance2161        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2162            .checked_sub(1)2163            .ok_or(Error::<T>::NumOverflow)?;2164        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21652166        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2167            .checked_add(1)2168            .ok_or(Error::<T>::NumOverflow)?;2169        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21702171        // change owner2172        let old_owner = item.owner.clone();2173        item.owner = new_owner.clone();2174        <NftItemList<T>>::insert(collection_id, item_id, item);21752176        // update index collection2177        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21782179        Ok(())2180    }2181    2182    fn set_re_fungible_variable_data(2183        collection: &Collection<T>,2184        item_id: TokenId,2185        data: Vec<u8>2186    ) -> DispatchResult {2187        let collection_id = collection.id;2188        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21892190        item.variable_data = data;21912192        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21932194        Ok(())2195    }21962197    fn set_nft_variable_data(2198        collection: &Collection<T>,2199        item_id: TokenId,2200        data: Vec<u8>2201    ) -> DispatchResult {2202        let collection_id = collection.id;2203        let mut item = <NftItemList<T>>::get(collection_id, item_id);2204        2205        item.variable_data = data;22062207        <NftItemList<T>>::insert(collection_id, item_id, item);2208        2209        Ok(())2210    }22112212    fn init_collection(item: &Collection<T>) {2213        // check params2214        assert!(2215            item.decimal_points <= MAX_DECIMAL_POINTS,2216            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2217        );2218        assert!(2219            item.name.len() <= 64,2220            "Collection name can not be longer than 63 char"2221        );2222        assert!(2223            item.name.len() <= 256,2224            "Collection description can not be longer than 255 char"2225        );2226        assert!(2227            item.token_prefix.len() <= 16,2228            "Token prefix can not be longer than 15 char"2229        );22302231        // Generate next collection ID2232        let next_id = CreatedCollectionCount::get()2233            .checked_add(1)2234            .unwrap();22352236        CreatedCollectionCount::put(next_id);2237    }22382239    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2240        let current_index = <ItemListIndex>::get(collection_id)2241            .checked_add(1)2242            .unwrap();22432244        let item_owner = item.owner.clone();2245        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22462247        <ItemListIndex>::insert(collection_id, current_index);22482249        // Update balance2250        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2251            .checked_add(1)2252            .unwrap();2253        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2254    }22552256    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2257        let current_index = <ItemListIndex>::get(collection_id)2258            .checked_add(1)2259            .unwrap();22602261        Self::add_token_index(collection_id, current_index, owner).unwrap();22622263        <ItemListIndex>::insert(collection_id, current_index);22642265        // Update balance2266        let new_balance = <Balance<T>>::get(collection_id, owner)2267            .checked_add(item.value)2268            .unwrap();2269        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2270    }22712272    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2273        let current_index = <ItemListIndex>::get(collection_id)2274            .checked_add(1)2275            .unwrap();22762277        let value = item.owner.first().unwrap().fraction;2278        let owner = item.owner.first().unwrap().owner.clone();22792280        Self::add_token_index(collection_id, current_index, &owner).unwrap();22812282        <ItemListIndex>::insert(collection_id, current_index);22832284        // Update balance2285        let new_balance = <Balance<T>>::get(collection_id, &owner)2286            .checked_add(value)2287            .unwrap();2288        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2289    }22902291    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2292        // add to account limit2293        if <AccountItemCount<T>>::contains_key(owner) {22942295            // bound Owned tokens by a single address2296            let count = <AccountItemCount<T>>::get(owner);2297            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22982299            <AccountItemCount<T>>::insert(owner.clone(), count2300                .checked_add(1)2301                .ok_or(Error::<T>::NumOverflow)?);2302        }2303        else {2304            <AccountItemCount<T>>::insert(owner.clone(), 1);2305        }23062307        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2308        if list_exists {2309            let mut list = <AddressTokens<T>>::get(collection_id, owner);2310            let item_contains = list.contains(&item_index.clone());23112312            if !item_contains {2313                list.push(item_index.clone());2314            }23152316            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2317        } else {2318            let mut itm = Vec::new();2319            itm.push(item_index.clone());2320            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2321        }23222323        Ok(())2324    }23252326    fn remove_token_index(2327        collection_id: CollectionId,2328        item_index: TokenId,2329        owner: &T::AccountId,2330    ) -> DispatchResult {23312332        // update counter2333        <AccountItemCount<T>>::insert(owner.clone(), 2334            <AccountItemCount<T>>::get(owner)2335            .checked_sub(1)2336            .ok_or(Error::<T>::NumOverflow)?);233723382339        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2340        if list_exists {2341            let mut list = <AddressTokens<T>>::get(collection_id, owner);2342            let item_contains = list.contains(&item_index.clone());23432344            if item_contains {2345                list.retain(|&item| item != item_index);2346                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2347            }2348        }23492350        Ok(())2351    }23522353    fn move_token_index(2354        collection_id: CollectionId,2355        item_index: TokenId,2356        old_owner: &T::AccountId,2357        new_owner: &T::AccountId,2358    ) -> DispatchResult {2359        Self::remove_token_index(collection_id, item_index, old_owner)?;2360        Self::add_token_index(collection_id, item_index, new_owner)?;23612362        Ok(())2363    }2364    2365    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2366        if <ContractOwner<T>>::contains_key(contract.clone()) {2367            let owner = <ContractOwner<T>>::get(contract);2368            ensure!(account == owner, Error::<T>::NoPermission);2369        } else {2370            fail!(Error::<T>::NoPermission);2371        }23722373        Ok(())2374    }2375}23762377////////////////////////////////////////////////////////////////////////////////////////////////////2378// Economic models2379// #region23802381/// Fee multiplier.2382pub type Multiplier = FixedU128;23832384type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;23852386/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2387/// in the queue.2388#[derive(Encode, Decode, Clone, Eq, PartialEq)]2389pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23902391impl<T: Config + Send + Sync> sp_std::fmt::Debug 2392    for ChargeTransactionPayment<T>2393{2394	#[cfg(feature = "std")]2395	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2396		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2397	}2398	#[cfg(not(feature = "std"))]2399	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2400		Ok(())2401	}2402}24032404impl<T: Config> ChargeTransactionPayment<T>2405where2406    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2407    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2408    T::AccountId: AsRef<[u8]>,2409    T::AccountId: UncheckedFrom<T::Hash>,2410{2411    fn traditional_fee(2412        len: usize,2413        info: &DispatchInfoOf<T::Call>,2414        tip: BalanceOf<T>,2415    ) -> BalanceOf<T>2416    where2417        T::Call: Dispatchable<Info = DispatchInfo>,2418    {2419        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2420    }24212422	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2423        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2424        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2425        let len_saturation = max_block_length as u64 / (len as u64).max(1);2426        let coefficient: BalanceOf<T> = weight_saturation2427            .min(len_saturation)2428            .saturated_into::<BalanceOf<T>>();2429        final_fee2430            .saturating_mul(coefficient)2431            .saturated_into::<TransactionPriority>()2432    }24332434    fn withdraw_fee(2435        &self,2436        who: &T::AccountId,2437        call: &T::Call,2438        info: &DispatchInfoOf<T::Call>,2439        len: usize,2440	) -> Result<2441		(2442			BalanceOf<T>,2443			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2444		),2445		TransactionValidityError,2446	> {2447        let tip = self.0;24482449        // Set fee based on call type. Creating collection costs 1 Unique.2450        // All other transactions have traditional fees so far2451        // let fee = match call.is_sub_type() {2452        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2453        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2454        //                                                 // _ => <BalanceOf<T>>::from(100)2455        // };2456        let fee = Self::traditional_fee(len, info, tip);24572458        // Only mess with balances if fee is not zero.2459        if fee.is_zero() {2460            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2461			.map(|i| (fee, i));2462        }24632464        // Determine who is paying transaction fee based on ecnomic model2465        // Parse call to extract collection ID and access collection sponsor2466        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2467            Some(Call::create_item(collection_id, _owner, _properties)) => {2468                let collection = <CollectionById<T>>::get(collection_id)?;24692470                // sponsor timeout2471                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24722473                let limit = collection.limits.sponsor_transfer_timeout;2474                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2475                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2476                    let limit_time = last_tx_block + limit.into();2477                    if block_number <= limit_time {2478                        return None;2479                    }2480                }2481                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);24822483                // check free create limit2484                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2485                   (collection.sponsor_confirmed)2486                {2487                    Some(collection.sponsor)2488                } else {2489                    None2490                }2491            }2492            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2493                let collection = <CollectionById<T>>::get(collection_id)?;2494                2495                let mut sponsor_transfer = false;2496                if collection.sponsor_confirmed {24972498                    let collection_limits = collection.limits;2499                    let collection_mode = collection.mode;2500    2501                    // sponsor timeout2502                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2503                    sponsor_transfer = match collection_mode {2504                        CollectionMode::NFT => {2505    2506                            // get correct limit2507                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2508                                collection_limits.sponsor_transfer_timeout2509                            } else {2510                                ChainLimit::get().nft_sponsor_transfer_timeout2511                            };2512    2513                            let mut sponsored = true;2514                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2515                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2516                                let limit_time = last_tx_block + limit.into();2517                                if block_number <= limit_time {2518                                    sponsored = false;2519                                }2520                            }2521                            if sponsored {2522                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2523                            }25242525                            sponsored2526                        }2527                        CollectionMode::Fungible(_) => {2528    2529                            // get correct limit2530                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2531                                collection_limits.sponsor_transfer_timeout2532                            } else {2533                                ChainLimit::get().fungible_sponsor_transfer_timeout2534                            };2535    2536                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2537                            let mut sponsored = true;2538                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2539                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2540                                let limit_time = last_tx_block + limit.into();2541                                if block_number <= limit_time {2542                                    sponsored = false;2543                                }2544                            }2545                            if sponsored {2546                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2547                            }25482549                            sponsored2550                        }2551                        CollectionMode::ReFungible => {2552    2553                            // get correct limit2554                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2555                                collection_limits.sponsor_transfer_timeout2556                            } else {2557                                ChainLimit::get().refungible_sponsor_transfer_timeout2558                            };2559    2560                            let mut sponsored = true;2561                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2562                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2563                                let limit_time = last_tx_block + limit.into();2564                                if block_number <= limit_time {2565                                    sponsored = false;2566                                }2567                            }2568                            if sponsored {2569                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2570                            }25712572                            sponsored2573                        }2574                        _ => {2575                            false2576                        },2577                    };2578                }25792580                if !sponsor_transfer {2581                    None2582                } else {2583                    Some(collection.sponsor)2584                }2585            }25862587            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2588                let mut sponsor_metadata_changes = false;25892590                let collection = <CollectionById<T>>::get(collection_id)?;25912592                if2593                    collection.sponsor_confirmed &&2594                    // Can't sponsor fungible collection, this tx will be rejected2595                    // as invalid2596                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2597                    data.len() <= collection.limits.sponsored_data_size as usize2598                {2599                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2600                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26012602                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2603                            .map(|last_block| block_number - last_block > rate_limit)2604                            .unwrap_or(true) 2605                        {2606                            sponsor_metadata_changes = true;2607                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2608                        }2609                    }2610                }26112612                if !sponsor_metadata_changes {2613                    None2614                } else {2615                    Some(collection.sponsor)2616                }2617            }26182619            _ => None,2620        })();26212622        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2623            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26242625                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26262627                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2628                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2629                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2630                  2631                if !owned_contract && white_list_enabled {2632                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2633                        return Err(InvalidTransaction::Call.into());2634                    }2635                }2636            },2637            _ => {},2638        }26392640        // Sponsor smart contracts2641        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26422643            // On instantiation: set the contract owner2644            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26452646                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2647                    &who,2648                    code_hash,2649                    salt,2650                );2651                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26522653                None2654            },26552656            // On instantiation with code: set the contract owner2657            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {26582659                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2660                    &who,2661                    &T::Hashing::hash(&_code),2662                    _salt,2663                );26642665                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26662667                T::AccountId::default()2668            }26692670            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2671            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26722673                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26742675                let mut sponsor_transfer = false;2676                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2677                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2678                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2679                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2680                    let limit_time = last_tx_block + rate_limit;26812682                    if block_number >= limit_time {2683                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2684                        sponsor_transfer = true;2685                    }2686                } else {2687                    sponsor_transfer = false;2688                }2689               2690                if sponsor_transfer {2691                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2692                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2693                            return Some(called_contract);2694                        }2695                    }2696                }26972698                None2699            },27002701            _ => None,2702        });27032704        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());27052706		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2707			.map(|i| (fee, i))2708    }2709}271027112712impl<T: Config + Send + Sync> SignedExtension2713    for ChargeTransactionPayment<T>2714where2715    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2716    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2717    T::AccountId: AsRef<[u8]>,2718    T::AccountId: UncheckedFrom<T::Hash>,2719{2720    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2721    type AccountId = T::AccountId;2722    type Call = T::Call;2723    type AdditionalSigned = ();2724    type Pre = (2725        // tip2726        BalanceOf<T>,2727        // who pays fee2728        Self::AccountId,2729		// imbalance resulting from withdrawing the fee2730		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2731    );2732    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2733        Ok(())2734    }27352736    fn validate(2737        &self,2738        who: &Self::AccountId,2739        call: &Self::Call,2740        info: &DispatchInfoOf<Self::Call>,2741        len: usize,2742    ) -> TransactionValidity {2743		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2744		Ok(ValidTransaction {2745			priority: Self::get_priority(len, info, fee),2746			..Default::default()2747		})2748    }27492750    fn pre_dispatch(2751        self,2752        who: &Self::AccountId,2753        call: &Self::Call,2754        info: &DispatchInfoOf<Self::Call>,2755        len: usize,2756    ) -> Result<Self::Pre, TransactionValidityError> {2757        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2758        Ok((self.0, who.clone(), imbalance))2759    }27602761    fn post_dispatch(2762        pre: Self::Pre,2763        info: &DispatchInfoOf<Self::Call>,2764        post_info: &PostDispatchInfoOf<Self::Call>,2765        len: usize,2766        _result: &DispatchResult,2767    ) -> Result<(), TransactionValidityError> {2768		let (tip, who, imbalance) = pre;2769		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2770			len as u32,2771			info,2772			post_info,2773			tip,2774		);2775		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2776		Ok(())2777    }2778}27792780// #endregion
after · pallets/nft/src/lib.rs
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use std::*;1213#[cfg(feature = "std")]14pub use serde::*;1516use codec::{Decode, Encode};17pub use frame_support::{18    construct_runtime, decl_event, decl_module, decl_storage, decl_error,19    dispatch::DispatchResult,20    ensure, fail, parameter_types,21    traits::{22        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,23        Randomness, IsSubType,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31};3233use frame_system::{self as system, ensure_signed, ensure_root};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_transaction_payment::OnChargeTransaction;4748#[cfg(test)]49mod mock;5051#[cfg(test)]52mod tests;5354mod default_weights;5556pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;57pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;58pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;59pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6061// Structs62// #region6364pub type CollectionId = u32;65pub type TokenId = u32;66pub type DecimalPoints = u8;6768#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]69#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]70pub enum CollectionMode {71    Invalid,72    NFT,73    // decimal points74    Fungible(DecimalPoints),75    ReFungible,76}7778impl Default for CollectionMode {79    fn default() -> Self {80        Self::Invalid81    }82}8384impl Into<u8> for CollectionMode {85    fn into(self) -> u8 {86        match self {87            CollectionMode::Invalid => 0,88            CollectionMode::NFT => 1,89            CollectionMode::Fungible(_) => 2,90            CollectionMode::ReFungible => 3,91        }92    }93}9495#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]96#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]97pub enum AccessMode {98    Normal,99    WhiteList,100}101impl Default for AccessMode {102    fn default() -> Self {103        Self::Normal104    }105}106107#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]108#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]109pub enum SchemaVersion {110    ImageURL,111    Unique,112}113impl Default for SchemaVersion {114    fn default() -> Self {115        Self::ImageURL116    }117}118119#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]120#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]121pub struct Ownership<AccountId> {122    pub owner: AccountId,123    pub fraction: u128,124}125126#[derive(Encode, Decode, Clone, PartialEq)]127#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]128pub struct Collection<T: Config> {129    pub id: CollectionId,130    pub owner: T::AccountId,131    pub mode: CollectionMode,132    pub access: AccessMode,133    pub decimal_points: DecimalPoints,134    pub name: Vec<u16>,        // 64 include null escape char135    pub description: Vec<u16>, // 256 include null escape char136    pub token_prefix: Vec<u8>, // 16 include null escape char137    pub mint_mode: bool,138    pub offchain_schema: Vec<u8>,139    pub schema_version: SchemaVersion,140    pub sponsor: T::AccountId, // Who pays fees. If set to default address, the fees are applied to the transaction sender141    pub sponsor_confirmed: bool, // False if sponsor address has not yet confirmed sponsorship. True otherwise.142    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 143    pub variable_on_chain_schema: Vec<u8>, //144    pub const_on_chain_schema: Vec<u8>, //145}146147#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]148#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]149pub struct NftItemType<AccountId> {150    pub owner: AccountId,151    pub const_data: Vec<u8>,152    pub variable_data: Vec<u8>,153}154155#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]156#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]157pub struct FungibleItemType {158    pub value: u128,159}160161#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]162#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]163pub struct ReFungibleItemType<AccountId> {164    pub owner: Vec<Ownership<AccountId>>,165    pub const_data: Vec<u8>,166    pub variable_data: Vec<u8>,167}168169// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]170// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]171// pub struct VestingItem<AccountId, Moment> {172//     pub sender: AccountId,173//     pub recipient: AccountId,174//     pub collection_id: CollectionId,175//     pub item_id: TokenId,176//     pub amount: u64,177//     pub vesting_date: Moment,178// }179180#[derive(Encode, Decode, Debug, Clone, PartialEq)]181#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]182pub struct CollectionLimits<BlockNumber: Encode + Decode> {183    pub account_token_ownership_limit: u32,184    pub sponsored_data_size: u32,185    /// None - setVariableMetadata is not sponsored186    /// Some(v) - setVariableMetadata is sponsored 187    ///           if there is v block between txs188    pub sponsored_data_rate_limit: Option<BlockNumber>,189    pub token_limit: u32,190191    // Timeouts for item types in passed blocks192    pub sponsor_transfer_timeout: u32,193    pub owner_can_transfer: bool,194    pub owner_can_destroy: bool,195}196197impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {198    fn default() -> Self {199        Self { 200            account_token_ownership_limit: 10_000_000, 201            token_limit: u32::max_value(),202            sponsored_data_size: u32::MAX, 203            sponsored_data_rate_limit: None,204            sponsor_transfer_timeout: 14400,205            owner_can_transfer: true,206            owner_can_destroy: true207        }208    }209}210211#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]212#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]213pub struct ChainLimits {214    pub collection_numbers_limit: u32,215    pub account_token_ownership_limit: u32,216    pub collections_admins_limit: u64,217    pub custom_data_limit: u32,218219    // Timeouts for item types in passed blocks220    pub nft_sponsor_transfer_timeout: u32,221    pub fungible_sponsor_transfer_timeout: u32,222    pub refungible_sponsor_transfer_timeout: u32,223224    // Schema limits225    pub offchain_schema_limit: u32,226    pub variable_on_chain_schema_limit: u32,227    pub const_on_chain_schema_limit: u32,228}229230pub trait WeightInfo {231	fn create_collection() -> Weight;232	fn destroy_collection() -> Weight;233	fn add_to_white_list() -> Weight;234	fn remove_from_white_list() -> Weight;235    fn set_public_access_mode() -> Weight;236    fn set_mint_permission() -> Weight;237    fn change_collection_owner() -> Weight;238    fn add_collection_admin() -> Weight;239    fn remove_collection_admin() -> Weight;240    fn set_collection_sponsor() -> Weight;241    fn confirm_sponsorship() -> Weight;242    fn remove_collection_sponsor() -> Weight;243    fn create_item(s: usize) -> Weight;244    fn burn_item() -> Weight;245    fn transfer() -> Weight;246    fn approve() -> Weight;247    fn transfer_from() -> Weight;248    fn set_offchain_schema() -> Weight;249    fn set_const_on_chain_schema() -> Weight;250    fn set_variable_on_chain_schema() -> Weight;251    fn set_variable_meta_data() -> Weight;252    fn enable_contract_sponsoring() -> Weight;253    fn set_schema_version() -> Weight;254    fn set_chain_limits() -> Weight;255    fn set_contract_sponsoring_rate_limit() -> Weight;256    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;257    fn toggle_contract_white_list() -> Weight;258    fn add_to_contract_white_list() -> Weight;259    fn remove_from_contract_white_list() -> Weight;260    fn set_collection_limits() -> Weight;261}262263#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]264#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]265pub struct CreateNftData {266    pub const_data: Vec<u8>,267    pub variable_data: Vec<u8>,268}269270#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]271#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]272pub struct CreateFungibleData {273    pub value: u128,274}275276#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CreateReFungibleData {279    pub const_data: Vec<u8>,280    pub variable_data: Vec<u8>,281    pub pieces: u128,282}283284#[derive(Encode, Decode, Debug, Clone, PartialEq)]285#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]286pub enum CreateItemData {287    NFT(CreateNftData),288    Fungible(CreateFungibleData),289    ReFungible(CreateReFungibleData),290}291292impl CreateItemData {293    pub fn len(&self) -> usize {294        let len = match self {295            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),296            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),297            _ => 0298        };299        300        return len;301    }302}303304impl From<CreateNftData> for CreateItemData {305    fn from(item: CreateNftData) -> Self {306        CreateItemData::NFT(item)307    }308}309310impl From<CreateReFungibleData> for CreateItemData {311    fn from(item: CreateReFungibleData) -> Self {312        CreateItemData::ReFungible(item)313    }314}315316impl From<CreateFungibleData> for CreateItemData {317    fn from(item: CreateFungibleData) -> Self {318        CreateItemData::Fungible(item)319    }320}321322323decl_error! {324	/// Error for non-fungible-token module.325	pub enum Error for Module<T: Config> {326        /// Total collections bound exceeded.327        TotalCollectionsLimitExceeded,328		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.329        CollectionDecimalPointLimitExceeded, 330        /// Collection name can not be longer than 63 char.331        CollectionNameLimitExceeded, 332        /// Collection description can not be longer than 255 char.333        CollectionDescriptionLimitExceeded, 334        /// Token prefix can not be longer than 15 char.335        CollectionTokenPrefixLimitExceeded,336        /// This collection does not exist.337        CollectionNotFound,338        /// Item not exists.339        TokenNotFound,340        /// Admin not found341        AdminNotFound,342        /// Arithmetic calculation overflow.343        NumOverflow,       344        /// Account already has admin role.345        AlreadyAdmin,  346        /// You do not own this collection.347        NoPermission,348        /// This address is not set as sponsor, use setCollectionSponsor first.349        ConfirmUnsetSponsorFail,350        /// Collection is not in mint mode.351        PublicMintingNotAllowed,352        /// Sender parameter and item owner must be equal.353        MustBeTokenOwner,354        /// Item balance not enough.355        TokenValueTooLow,356        /// Size of item is too large.357        NftSizeLimitExceeded,358        /// No approve found359        ApproveNotFound,360        /// Requested value more than approved.361        TokenValueNotEnough,362        /// Only approved addresses can call this method.363        ApproveRequired,364        /// Address is not in white list.365        AddresNotInWhiteList,366        /// Number of collection admins bound exceeded.367        CollectionAdminsLimitExceeded,368        /// Owned tokens by a single address bound exceeded.369        AddressOwnershipLimitExceeded,370        /// Length of items properties must be greater than 0.371        EmptyArgument,372        /// const_data exceeded data limit.373        TokenConstDataLimitExceeded,374        /// variable_data exceeded data limit.375        TokenVariableDataLimitExceeded,376        /// Not NFT item data used to mint in NFT collection.377        NotNftDataUsedToMintNftCollectionToken,378        /// Not Fungible item data used to mint in Fungible collection.379        NotFungibleDataUsedToMintFungibleCollectionToken,380        /// Not Re Fungible item data used to mint in Re Fungible collection.381        NotReFungibleDataUsedToMintReFungibleCollectionToken,382        /// Unexpected collection type.383        UnexpectedCollectionType,384        /// Can't store metadata in fungible tokens.385        CantStoreMetadataInFungibleTokens,386        /// Collection token limit exceeded387        CollectionTokenLimitExceeded,388        /// Account token limit exceeded per collection389        AccountTokenLimitExceeded,390        /// Collection limit bounds per collection exceeded391        CollectionLimitBoundsExceeded,392        /// Tried to enable permissions which are only permitted to be disabled393        OwnerPermissionsCantBeReverted,394        /// Schema data size limit bound exceeded395        SchemaDataLimitExceeded,396        /// Maximum refungibility exceeded397        WrongRefungiblePieces398	}399}400401pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {402    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;403404    /// Weight information for extrinsics in this pallet.405	type WeightInfo: WeightInfo;406}407408#[cfg(feature = "runtime-benchmarks")]409mod benchmarking;410411// #endregion412413decl_storage! {414    trait Store for Module<T: Config> as Nft {415416        // Private members417        NextCollectionID: CollectionId;418        CreatedCollectionCount: u32;419        ChainVersion: u64;420        ItemListIndex: map hasher(identity) CollectionId => TokenId;421422        // Chain limits struct423        pub ChainLimit get(fn chain_limit) config(): ChainLimits;424425        // Bound counters426        CollectionCount: u32;427        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;428429        // Basic collections430        pub CollectionById get(fn collection_id) config(): map hasher(identity) CollectionId => Option<Collection<T>> = None;431        pub AdminList get(fn admin_list_collection): map hasher(identity) CollectionId => Vec<T::AccountId>;432        pub WhiteList get(fn white_list): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => bool;433434        /// Balance owner per collection map435        pub Balance get(fn balance_count): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => u128;436437        /// second parameter: item id + owner account id + spender account id438        pub Allowances get(fn approved): double_map hasher(identity) CollectionId, hasher(twox_64_concat) (TokenId, T::AccountId, T::AccountId) => u128;439440        /// Item collections441        pub NftItemList get(fn nft_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => NftItemType<T::AccountId>;442        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => FungibleItemType;443        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(identity) CollectionId, hasher(identity) TokenId => ReFungibleItemType<T::AccountId>;444445        /// Index list446        pub AddressTokens get(fn address_tokens): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => Vec<TokenId>;447448        /// Tokens transfer baskets449        pub CreateItemBasket get(fn create_item_basket): map hasher(twox_64_concat) (CollectionId, T::AccountId) => T::BlockNumber;450        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;451        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;452        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => T::BlockNumber;453454        /// Variable metadata sponsoring455        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(identity) CollectionId, hasher(identity) TokenId => Option<T::BlockNumber> = None;456457        // Contract Sponsorship and Ownership458        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => T::AccountId;459        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;460        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;461        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;462        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 463        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(twox_64_concat) T::AccountId => bool; 464    }465    add_extra_genesis {466        build(|config: &GenesisConfig<T>| {467            // Modification of storage468            for (_num, _c) in &config.collection_id {469                <Module<T>>::init_collection(_c);470            }471472            for (_num, _c, _i) in &config.nft_item_id {473                <Module<T>>::init_nft_token(*_c, _i);474            }475476            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {477                <Module<T>>::init_fungible_token(*collection_id, account_id, fungible_item);478            }479480            for (_num, _c, _i) in &config.refungible_item_id {481                <Module<T>>::init_refungible_token(*_c, _i);482            }483        })484    }485}486487decl_event!(488    pub enum Event<T>489    where490        AccountId = <T as system::Config>::AccountId,491    {492        /// New collection was created493        /// 494        /// # Arguments495        /// 496        /// * collection_id: Globally unique identifier of newly created collection.497        /// 498        /// * mode: [CollectionMode] converted into u8.499        /// 500        /// * account_id: Collection owner.501        Created(CollectionId, u8, AccountId),502503        /// New item was created.504        /// 505        /// # Arguments506        /// 507        /// * collection_id: Id of the collection where item was created.508        /// 509        /// * item_id: Id of an item. Unique within the collection.510        ///511        /// * recipient: Owner of newly created item 512        ItemCreated(CollectionId, TokenId, AccountId),513514        /// Collection item was burned.515        /// 516        /// # Arguments517        /// 518        /// collection_id.519        /// 520        /// item_id: Identifier of burned NFT.521        ItemDestroyed(CollectionId, TokenId),522523        /// Item was transferred524        ///525        /// * collection_id: Id of collection to which item is belong526        ///527        /// * item_id: Id of an item528        ///529        /// * sender: Original owner of item530        ///531        /// * recipient: New owner of item532        ///533        /// * amount: Always 1 for NFT534        Transfer(CollectionId, TokenId, AccountId, AccountId, u128),535    }536);537538decl_module! {539    pub struct Module<T: Config> for enum Call 540    where 541        origin: T::Origin542    {543        fn deposit_event() = default;544        type Error = Error<T>;545546        fn on_initialize(now: T::BlockNumber) -> Weight {547548            if ChainVersion::get() < 2549            {550                let value = NextCollectionID::get();551                CreatedCollectionCount::put(value);552                ChainVersion::put(2);553            }554555            0556        }557558        /// 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.559        /// 560        /// # Permissions561        /// 562        /// * Anyone.563        /// 564        /// # Arguments565        /// 566        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.567        /// 568        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.569        /// 570        /// * token_prefix: UTF-8 string with token prefix.571        /// 572        /// * mode: [CollectionMode] collection type and type dependent data.573        // returns collection ID574        #[weight = <T as Config>::WeightInfo::create_collection()]575        pub fn create_collection(origin,576                                 collection_name: Vec<u16>,577                                 collection_description: Vec<u16>,578                                 token_prefix: Vec<u8>,579                                 mode: CollectionMode) -> DispatchResult {580581            // Anyone can create a collection582            let who = ensure_signed(origin)?;583584            let decimal_points = match mode {585                CollectionMode::Fungible(points) => points,586                _ => 0587            };588589            let chain_limit = ChainLimit::get();590591            // bound Total number of collections592            ensure!(CollectionCount::get() < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);593594            // check params595            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);596            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);597            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);598            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);599600            // Generate next collection ID601            let next_id = CreatedCollectionCount::get()602                .checked_add(1)603                .ok_or(Error::<T>::NumOverflow)?;604605            // bound counter606            let total = CollectionCount::get()607                .checked_add(1)608                .ok_or(Error::<T>::NumOverflow)?;609610            CreatedCollectionCount::put(next_id);611            CollectionCount::put(total);612613            let limits = CollectionLimits {614                sponsored_data_size: chain_limit.custom_data_limit,615                ..Default::default()616            };617618            // Create new collection619            let new_collection = Collection {620                id: next_id,621                owner: who.clone(),622                name: collection_name,623                mode: mode.clone(),624                mint_mode: false,625                access: AccessMode::Normal,626                description: collection_description,627                decimal_points: decimal_points,628                token_prefix: token_prefix,629                offchain_schema: Vec::new(),630                schema_version: SchemaVersion::ImageURL,631                sponsor: T::AccountId::default(),632                sponsor_confirmed: false,633                variable_on_chain_schema: Vec::new(),634                const_on_chain_schema: Vec::new(),635                limits,636            };637638            // Add new collection to map639            <CollectionById<T>>::insert(next_id, new_collection);640641            // call event642            Self::deposit_event(RawEvent::Created(next_id, mode.into(), who.clone()));643644            Ok(())645        }646647        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.648        /// 649        /// # Permissions650        /// 651        /// * Collection Owner.652        /// 653        /// # Arguments654        /// 655        /// * collection_id: collection to destroy.656        #[weight = <T as Config>::WeightInfo::destroy_collection()]657        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {658659            let sender = ensure_signed(origin)?;660            let collection = Self::get_collection(collection_id)?;661            Self::check_owner_permissions(&collection, sender)?;662            if !collection.limits.owner_can_destroy {663                fail!(Error::<T>::NoPermission);664            }665666            <AddressTokens<T>>::remove_prefix(collection_id);667            <Allowances<T>>::remove_prefix(collection_id);668            <Balance<T>>::remove_prefix(collection_id);669            <ItemListIndex>::remove(collection_id);670            <AdminList<T>>::remove(collection_id);671            <CollectionById<T>>::remove(collection_id);672            <WhiteList<T>>::remove_prefix(collection_id);673674            <NftItemList<T>>::remove_prefix(collection_id);675            <FungibleItemList<T>>::remove_prefix(collection_id);676            <ReFungibleItemList<T>>::remove_prefix(collection_id);677678            <NftTransferBasket<T>>::remove_prefix(collection_id);679            <FungibleTransferBasket<T>>::remove_prefix(collection_id);680            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);681682            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);683684            if CollectionCount::get() > 0685            {686                // bound couter687                let total = CollectionCount::get()688                    .checked_sub(1)689                    .ok_or(Error::<T>::NumOverflow)?;690691                CollectionCount::put(total);692            }693694            Ok(())695        }696697        /// Add an address to white list.698        /// 699        /// # Permissions700        /// 701        /// * Collection Owner702        /// * Collection Admin703        /// 704        /// # Arguments705        /// 706        /// * collection_id.707        /// 708        /// * address.709        #[weight = <T as Config>::WeightInfo::add_to_white_list()]710        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{711712            let sender = ensure_signed(origin)?;713            let collection = Self::get_collection(collection_id)?;714            Self::check_owner_or_admin_permissions(&collection, sender)?;715716            <WhiteList<T>>::insert(collection_id, address, true);717            718            Ok(())719        }720721        /// Remove an address from white list.722        /// 723        /// # Permissions724        /// 725        /// * Collection Owner726        /// * Collection Admin727        /// 728        /// # Arguments729        /// 730        /// * collection_id.731        /// 732        /// * address.733        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]734        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::AccountId) -> DispatchResult{735736            let sender = ensure_signed(origin)?;737            let collection = Self::get_collection(collection_id)?;738            Self::check_owner_or_admin_permissions(&collection, sender)?;739740            <WhiteList<T>>::remove(collection_id, address);741742            Ok(())743        }744745        /// Toggle between normal and white list access for the methods with access for `Anyone`.746        /// 747        /// # Permissions748        /// 749        /// * Collection Owner.750        /// 751        /// # Arguments752        /// 753        /// * collection_id.754        /// 755        /// * mode: [AccessMode]756        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]757        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult758        {759            let sender = ensure_signed(origin)?;760761            let mut target_collection = Self::get_collection(collection_id)?;762            Self::check_owner_permissions(&target_collection, sender)?;763            target_collection.access = mode;764            Self::save_collection(target_collection);765766            Ok(())767        }768769        /// Allows Anyone to create tokens if:770        /// * White List is enabled, and771        /// * Address is added to white list, and772        /// * This method was called with True parameter773        /// 774        /// # Permissions775        /// * Collection Owner776        ///777        /// # Arguments778        /// 779        /// * collection_id.780        /// 781        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.782        #[weight = <T as Config>::WeightInfo::set_mint_permission()]783        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult784        {785            let sender = ensure_signed(origin)?;786787            let mut target_collection = Self::get_collection(collection_id)?;788            Self::check_owner_permissions(&target_collection, sender)?;789            target_collection.mint_mode = mint_permission;790            Self::save_collection(target_collection);791792            Ok(())793        }794795        /// Change the owner of the collection.796        /// 797        /// # Permissions798        /// 799        /// * Collection Owner.800        /// 801        /// # Arguments802        /// 803        /// * collection_id.804        /// 805        /// * new_owner.806        #[weight = <T as Config>::WeightInfo::change_collection_owner()]807        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {808809            let sender = ensure_signed(origin)?;810            let mut target_collection = Self::get_collection(collection_id)?;811            Self::check_owner_permissions(&target_collection, sender)?;812            target_collection.owner = new_owner;813            Self::save_collection(target_collection);814815            Ok(())816        }817818        /// Adds an admin of the Collection.819        /// 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. 820        /// 821        /// # Permissions822        /// 823        /// * Collection Owner.824        /// * Collection Admin.825        /// 826        /// # Arguments827        /// 828        /// * collection_id: ID of the Collection to add admin for.829        /// 830        /// * new_admin_id: Address of new admin to add.831        #[weight = <T as Config>::WeightInfo::add_collection_admin()]832        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::AccountId) -> DispatchResult {833834            let sender = ensure_signed(origin)?;835            let collection = Self::get_collection(collection_id)?;836            Self::check_owner_or_admin_permissions(&collection, sender)?;837            let mut admin_arr: Vec<T::AccountId> = Vec::new();838839            if <AdminList<T>>::contains_key(collection_id)840            {841                admin_arr = <AdminList<T>>::get(collection_id);842                ensure!(!admin_arr.contains(&new_admin_id), Error::<T>::AlreadyAdmin);843            }844845            // Number of collection admins846            ensure!((admin_arr.len() as u64) < ChainLimit::get().collections_admins_limit, Error::<T>::CollectionAdminsLimitExceeded);847848            admin_arr.push(new_admin_id);849            <AdminList<T>>::insert(collection_id, admin_arr);850851            Ok(())852        }853854        /// 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.855        ///856        /// # Permissions857        /// 858        /// * Collection Owner.859        /// * Collection Admin.860        /// 861        /// # Arguments862        /// 863        /// * collection_id: ID of the Collection to remove admin for.864        /// 865        /// * account_id: Address of admin to remove.866        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]867        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::AccountId) -> DispatchResult {868869            let sender = ensure_signed(origin)?;870            let collection = Self::get_collection(collection_id)?;871            Self::check_owner_or_admin_permissions(&collection, sender)?;872            ensure!(<AdminList<T>>::contains_key(collection_id), Error::<T>::AdminNotFound);873874            let mut admin_arr = <AdminList<T>>::get(collection_id);875            admin_arr.retain(|i| *i != account_id);876            <AdminList<T>>::insert(collection_id, admin_arr);877878            Ok(())879        }880881        /// # Permissions882        /// 883        /// * Collection Owner884        /// 885        /// # Arguments886        /// 887        /// * collection_id.888        /// 889        /// * new_sponsor.890        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]891        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {892893            let sender = ensure_signed(origin)?;894            let mut target_collection = Self::get_collection(collection_id)?;895            Self::check_owner_permissions(&target_collection, sender)?;896897            target_collection.sponsor = new_sponsor;898            target_collection.sponsor_confirmed = false;899            Self::save_collection(target_collection);900901            Ok(())902        }903904        /// # Permissions905        /// 906        /// * Sponsor.907        /// 908        /// # Arguments909        /// 910        /// * collection_id.911        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]912        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {913914            let sender = ensure_signed(origin)?;915916            let mut target_collection = Self::get_collection(collection_id)?;917            ensure!(sender == target_collection.sponsor, Error::<T>::ConfirmUnsetSponsorFail);918919            target_collection.sponsor_confirmed = true;920            Self::save_collection(target_collection);921922            Ok(())923        }924925        /// Switch back to pay-per-own-transaction model.926        ///927        /// # Permissions928        ///929        /// * Collection owner.930        /// 931        /// # Arguments932        /// 933        /// * collection_id.934        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]935        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {936937            let sender = ensure_signed(origin)?;938939            let mut target_collection = Self::get_collection(collection_id)?;940            Self::check_owner_permissions(&target_collection, sender)?;941942            target_collection.sponsor = T::AccountId::default();943            target_collection.sponsor_confirmed = false;944            Self::save_collection(target_collection);945946            Ok(())947        }948949        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.950        /// 951        /// # Permissions952        /// 953        /// * Collection Owner.954        /// * Collection Admin.955        /// * Anyone if956        ///     * White List is enabled, and957        ///     * Address is added to white list, and958        ///     * MintPermission is enabled (see SetMintPermission method)959        /// 960        /// # Arguments961        /// 962        /// * collection_id: ID of the collection.963        /// 964        /// * owner: Address, initial owner of the NFT.965        ///966        /// * data: Token data to store on chain.967        // #[weight =968        // (130_000_000 as Weight)969        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))970        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))971        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]972973        #[weight = <T as Config>::WeightInfo::create_item(data.len())]974        pub fn create_item(origin, collection_id: CollectionId, owner: T::AccountId, data: CreateItemData) -> DispatchResult {975976            let sender = ensure_signed(origin)?;977978            let target_collection = Self::get_collection(collection_id)?;979980            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;981            Self::validate_create_item_args(&target_collection, &data)?;982            Self::create_item_no_validation(&target_collection, owner, data)?;983984            Ok(())985        }986987        /// This method creates multiple instances of NFT Collection created with CreateCollection method.988        /// 989        /// # Permissions990        /// 991        /// * Collection Owner.992        /// * Collection Admin.993        /// * Anyone if994        ///     * White List is enabled, and995        ///     * Address is added to white list, and996        ///     * MintPermission is enabled (see SetMintPermission method)997        /// 998        /// # Arguments999        /// 1000        /// * collection_id: ID of the collection.1001        /// 1002        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1003        /// 1004        /// * owner: Address, initial owner of the NFT.1005        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1006                               .map(|data| { data.len() })1007                               .sum())]1008        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::AccountId, items_data: Vec<CreateItemData>) -> DispatchResult {10091010            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1011            let sender = ensure_signed(origin)?;10121013            let target_collection = Self::get_collection(collection_id)?;10141015            Self::can_create_items_in_collection(&target_collection, &sender, &owner)?;10161017            for data in &items_data {1018                Self::validate_create_item_args(&target_collection, data)?;1019            }1020            for data in &items_data {1021                Self::create_item_no_validation(&target_collection, owner.clone(), data.clone())?;1022            }10231024            Ok(())1025        }10261027        /// Destroys a concrete instance of NFT.1028        /// 1029        /// # Permissions1030        /// 1031        /// * Collection Owner.1032        /// * Collection Admin.1033        /// * Current NFT Owner.1034        /// 1035        /// # Arguments1036        /// 1037        /// * collection_id: ID of the collection.1038        /// 1039        /// * item_id: ID of NFT to burn.1040        #[weight = <T as Config>::WeightInfo::burn_item()]1041        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {10421043            let sender = ensure_signed(origin)?;10441045            // Transfer permissions check1046            let target_collection = Self::get_collection(collection_id)?;1047            ensure!(1048                Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1049                (1050                    target_collection.limits.owner_can_transfer &&1051                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1052                ),1053                Error::<T>::NoPermission1054            );10551056            if target_collection.access == AccessMode::WhiteList {1057                Self::check_white_list(&target_collection, &sender)?;1058            }10591060            match target_collection.mode1061            {1062                CollectionMode::NFT => Self::burn_nft_item(&target_collection, item_id)?,1063                CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &target_collection, value)?,1064                CollectionMode::ReFungible  => Self::burn_refungible_item(&target_collection, item_id, &sender)?,1065                _ => ()1066            };10671068            // call event1069            Self::deposit_event(RawEvent::ItemDestroyed(target_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            let collection = Self::get_collection(collection_id)?;11011102            Self::transfer_internal(sender, recipient, &collection, item_id, value)1103        }11041105        /// Set, change, or remove approved address to transfer the ownership of the NFT.1106        /// 1107        /// # Permissions1108        /// 1109        /// * Collection Owner1110        /// * Collection Admin1111        /// * Current NFT owner1112        /// 1113        /// # Arguments1114        /// 1115        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1116        /// 1117        /// * collection_id.1118        /// 1119        /// * item_id: ID of the item.1120        #[weight = <T as Config>::WeightInfo::approve()]1121        pub fn approve(origin, spender: T::AccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {11221123            let sender = ensure_signed(origin)?;1124            let target_collection = Self::get_collection(collection_id)?;11251126            Self::token_exists(&target_collection, item_id, &sender)?;11271128            // Transfer permissions check1129            let bypasses_limits = target_collection.limits.owner_can_transfer &&1130                Self::is_owner_or_admin_permissions(1131                    &target_collection,1132                    sender.clone(),1133                );11341135            let allowance_limit = if bypasses_limits {1136                None1137            } else if let Some(amount) = Self::owned_amount(1138                sender.clone(),1139                &target_collection,1140                item_id,1141            ) {1142                Some(amount)1143            } else {1144                fail!(Error::<T>::NoPermission);1145            };11461147            if target_collection.access == AccessMode::WhiteList {1148                Self::check_white_list(&target_collection, &sender)?;1149                Self::check_white_list(&target_collection, &spender)?;1150            }11511152            let allowance_exists = <Allowances<T>>::contains_key(collection_id, (item_id, &sender, &spender));1153            let mut allowance: u128 = amount;1154            if allowance_exists {1155                allowance += <Allowances<T>>::get(collection_id, (item_id, &sender, &spender));1156            }1157            if let Some(limit) = allowance_limit {1158                ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1159            }1160            <Allowances<T>>::insert(collection_id, (item_id, sender.clone(), spender.clone()), allowance);11611162            Ok(())1163        }1164        1165        /// 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.1166        /// 1167        /// # Permissions1168        /// * Collection Owner1169        /// * Collection Admin1170        /// * Current NFT owner1171        /// * Address approved by current NFT owner1172        /// 1173        /// # Arguments1174        /// 1175        /// * from: Address that owns token.1176        /// 1177        /// * recipient: Address of token recipient.1178        /// 1179        /// * collection_id.1180        /// 1181        /// * item_id: ID of the item.1182        /// 1183        /// * value: Amount to transfer.1184        #[weight = <T as Config>::WeightInfo::transfer_from()]1185        pub fn transfer_from(origin, from: T::AccountId, recipient: T::AccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {11861187            let sender = ensure_signed(origin)?;1188            let target_collection = Self::get_collection(collection_id)?;11891190            let mut appoved_transfer = false;11911192            // Check approval1193            let mut approval: u128 = 0;1194            if <Allowances<T>>::contains_key(collection_id, (item_id, &from, &sender)) {1195                approval = <Allowances<T>>::get(collection_id, (item_id, &from, &sender));1196                ensure!(approval >= value, Error::<T>::TokenValueNotEnough);1197                appoved_transfer = true;1198            }11991200            // Limits check1201            Self::is_correct_transfer(&target_collection, &recipient)?;12021203            // Transfer permissions check         1204            ensure!(1205                appoved_transfer || 1206                (1207                    target_collection.limits.owner_can_transfer &&1208                    Self::is_owner_or_admin_permissions(&target_collection, sender.clone())1209                ),1210                Error::<T>::NoPermission1211            );12121213            if target_collection.access == AccessMode::WhiteList {1214                Self::check_white_list(&target_collection, &sender)?;1215                Self::check_white_list(&target_collection, &recipient)?;1216            }12171218            // Reduce approval by transferred amount or remove if remaining approval drops to 01219            if approval.checked_sub(value).unwrap_or(0) > 0 {1220                <Allowances<T>>::insert(collection_id, (item_id, &from, &sender), approval - value);1221            }1222            else {1223                <Allowances<T>>::remove(collection_id, (item_id, &from, &sender));1224            }12251226            match target_collection.mode1227            {1228                CollectionMode::NFT => Self::transfer_nft(&target_collection, item_id, from, recipient)?,1229                CollectionMode::Fungible(_)  => Self::transfer_fungible(&target_collection, value, &from, &recipient)?,1230                CollectionMode::ReFungible  => Self::transfer_refungible(&target_collection, item_id, value, from.clone(), recipient)?,1231                _ => ()1232            };12331234            Ok(())1235        }12361237        // #[weight = 0]1238        // pub fn safe_transfer_from(origin, collection_id: CollectionId, item_id: TokenId, new_owner: T::AccountId) -> DispatchResult {12391240        //     // let no_perm_mes = "You do not have permissions to modify this collection";1241        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1242        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1243        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);12441245        //     // // on_nft_received  call12461247        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;12481249        //     Ok(())1250        // }12511252        /// Set off-chain data schema.1253        /// 1254        /// # Permissions1255        /// 1256        /// * Collection Owner1257        /// * Collection Admin1258        /// 1259        /// # Arguments1260        /// 1261        /// * collection_id.1262        /// 1263        /// * schema: String representing the offchain data schema.1264        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1265        pub fn set_variable_meta_data (1266            origin,1267            collection_id: CollectionId,1268            item_id: TokenId,1269            data: Vec<u8>1270        ) -> DispatchResult {1271            let sender = ensure_signed(origin)?;1272            1273            let target_collection = Self::get_collection(collection_id)?;1274            Self::token_exists(&target_collection, item_id, &sender)?;12751276            ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);12771278            // Modify permissions check1279            ensure!(Self::is_item_owner(sender.clone(), &target_collection, item_id) ||1280                Self::is_owner_or_admin_permissions(&target_collection, sender.clone()),1281                Error::<T>::NoPermission);12821283            match target_collection.mode1284            {1285                CollectionMode::NFT => Self::set_nft_variable_data(&target_collection, item_id, data)?,1286                CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&target_collection, 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            let mut target_collection = Self::get_collection(collection_id)?;1316            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;1317            target_collection.schema_version = version;1318            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1343            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13441345            // check schema limit1346            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");13471348            target_collection.offchain_schema = schema;1349            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1374            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;13751376            // check schema limit1377            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");13781379            target_collection.const_on_chain_schema = schema;1380            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1405            Self::check_owner_or_admin_permissions(&target_collection, sender.clone())?;14061407            // check schema limit1408            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14091410            target_collection.variable_on_chain_schema = schema;1411            Self::save_collection(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            let mut target_collection = Self::get_collection(collection_id)?;1581            Self::check_owner_permissions(&target_collection, sender.clone())?;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            Self::save_collection(target_collection);16031604            Ok(())1605        } 1606    }1607}16081609impl<T: Config> Module<T> {16101611    pub fn transfer_internal(sender: T::AccountId, recipient: T::AccountId, target_collection: &Collection<T>, item_id: TokenId, value: u128) -> DispatchResult {1612        // Limits check1613        Self::is_correct_transfer(target_collection, &recipient)?;16141615        // Transfer permissions check1616        ensure!(Self::is_item_owner(sender.clone(), target_collection, item_id) ||1617            Self::is_owner_or_admin_permissions(target_collection, sender.clone()),1618            Error::<T>::NoPermission);16191620        if target_collection.access == AccessMode::WhiteList {1621            Self::check_white_list(target_collection, &sender)?;1622            Self::check_white_list(target_collection, &recipient)?;1623        }16241625        match target_collection.mode1626        {1627            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1628            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1629            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1630            _ => ()1631        };16321633        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender, recipient, value));16341635        Ok(())1636    }163716381639    fn is_correct_transfer(collection: &Collection<T>, recipient: &T::AccountId) -> DispatchResult {1640        let collection_id = collection.id;16411642        // check token limit and account token limit1643        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient).len() as u32;1644        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1645        1646        Ok(())1647    }16481649    fn can_create_items_in_collection(collection: &Collection<T>, sender: &T::AccountId, owner: &T::AccountId) -> DispatchResult {1650        let collection_id = collection.id;16511652        // check token limit and account token limit1653        let total_items: u32 = ItemListIndex::get(collection_id);1654        let account_items: u32 = <AddressTokens<T>>::get(collection_id, owner).len() as u32;1655        ensure!(collection.limits.token_limit > total_items,  Error::<T>::CollectionTokenLimitExceeded);1656        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);16571658        if !Self::is_owner_or_admin_permissions(collection, sender.clone()) {1659            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1660            Self::check_white_list(collection, owner)?;1661            Self::check_white_list(collection, sender)?;1662        }16631664        Ok(())1665    }16661667    fn validate_create_item_args(target_collection: &Collection<T>, data: &CreateItemData) -> DispatchResult {1668        match target_collection.mode1669        {1670            CollectionMode::NFT => {1671                if let CreateItemData::NFT(data) = data {1672                    // check sizes1673                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1674                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1675                } else {1676                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1677                }1678            },1679            CollectionMode::Fungible(_) => {1680                if let CreateItemData::Fungible(_) = data {1681                } else {1682                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1683                }1684            },1685            CollectionMode::ReFungible => {1686                if let CreateItemData::ReFungible(data) = data {16871688                    // check sizes1689                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1690                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);16911692                    // Check refungibility limits1693                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1694                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1695                } else {1696                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1697                }1698            },1699            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1700        };17011702        Ok(())1703    }17041705    fn create_item_no_validation(collection: &Collection<T>, owner: T::AccountId, data: CreateItemData) -> DispatchResult {1706        let collection_id = collection.id;17071708        match data1709        {1710            CreateItemData::NFT(data) => {1711                let item = NftItemType {1712                    owner: owner.clone(),1713                    const_data: data.const_data,1714                    variable_data: data.variable_data1715                };17161717                Self::add_nft_item(collection, item)?;1718            },1719            CreateItemData::Fungible(data) => {1720                Self::add_fungible_item(collection, &owner, data.value)?;1721            },1722            CreateItemData::ReFungible(data) => {1723                let mut owner_list = Vec::new();1724                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});17251726                let item = ReFungibleItemType {1727                    owner: owner_list,1728                    const_data: data.const_data,1729                    variable_data: data.variable_data1730                };17311732                Self::add_refungible_item(collection, item)?;1733            }1734        };17351736        // call event1737        Self::deposit_event(RawEvent::ItemCreated(collection_id, <ItemListIndex>::get(collection_id), owner));17381739        Ok(())1740    }17411742    fn add_fungible_item(collection: &Collection<T>, owner: &T::AccountId, value: u128) -> DispatchResult {1743        let collection_id = collection.id;17441745        // Does new owner already have an account?1746        let mut balance: u128 = 0;1747        if <FungibleItemList<T>>::contains_key(collection_id, owner) {1748            balance = <FungibleItemList<T>>::get(collection_id, owner).value;1749        } 17501751        // Mint 1752        let item = FungibleItemType {1753            value: balance + value1754        };1755        <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), item);17561757        // Update balance1758        let new_balance = <Balance<T>>::get(collection_id, owner)1759            .checked_add(value)1760            .ok_or(Error::<T>::NumOverflow)?;1761        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);17621763        Ok(())1764    }17651766    fn add_refungible_item(collection: &Collection<T>, item: ReFungibleItemType<T::AccountId>) -> DispatchResult {1767        let collection_id = collection.id;17681769        let current_index = <ItemListIndex>::get(collection_id)1770            .checked_add(1)1771            .ok_or(Error::<T>::NumOverflow)?;1772        let itemcopy = item.clone();17731774        let value = item.owner.first().unwrap().fraction;1775        let owner = item.owner.first().unwrap().owner.clone();17761777        Self::add_token_index(collection_id, current_index, &owner)?;17781779        <ItemListIndex>::insert(collection_id, current_index);1780        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);17811782        // Update balance1783        let new_balance = <Balance<T>>::get(collection_id, &owner)1784            .checked_add(value)1785            .ok_or(Error::<T>::NumOverflow)?;1786        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);17871788        Ok(())1789    }17901791    fn add_nft_item(collection: &Collection<T>, item: NftItemType<T::AccountId>) -> DispatchResult {1792        let collection_id = collection.id;17931794        let current_index = <ItemListIndex>::get(collection_id)1795            .checked_add(1)1796            .ok_or(Error::<T>::NumOverflow)?;17971798        let item_owner = item.owner.clone();1799        Self::add_token_index(collection_id, current_index, &item.owner)?;18001801        <ItemListIndex>::insert(collection_id, current_index);1802        <NftItemList<T>>::insert(collection_id, current_index, item);18031804        // Update balance1805        let new_balance = <Balance<T>>::get(collection_id, item_owner.clone())1806            .checked_add(1)1807            .ok_or(Error::<T>::NumOverflow)?;1808        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);18091810        Ok(())1811    }18121813    fn burn_refungible_item(1814        collection: &Collection<T>,1815        item_id: TokenId,1816        owner: &T::AccountId,1817    ) -> DispatchResult {1818        let collection_id = collection.id;18191820        ensure!(1821            <ReFungibleItemList<T>>::contains_key(collection_id, item_id),1822            Error::<T>::TokenNotFound1823        );1824        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id);1825        let rft_balance = token1826            .owner1827            .iter()1828            .filter(|&i| i.owner == *owner)1829            .next()1830            .unwrap();1831        Self::remove_token_index(collection_id, item_id, owner)?;18321833        // update balance1834        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.clone())1835            .checked_sub(rft_balance.fraction)1836            .ok_or(Error::<T>::NumOverflow)?;1837        <Balance<T>>::insert(collection_id, rft_balance.owner.clone(), new_balance);18381839        // Re-create owners list with sender removed1840        let index = token1841            .owner1842            .iter()1843            .position(|i| i.owner == *owner)1844            .unwrap();1845        token.owner.remove(index);1846        let owner_count = token.owner.len();18471848        // Burn the token completely if this was the last (only) owner1849        if owner_count == 0 {1850            <ReFungibleItemList<T>>::remove(collection_id, item_id);1851            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);1852        }1853        else {1854            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);1855        }18561857        Ok(())1858    }18591860    fn burn_nft_item(collection: &Collection<T>, item_id: TokenId) -> DispatchResult {1861        let collection_id = collection.id;18621863        ensure!(1864            <NftItemList<T>>::contains_key(collection_id, item_id),1865            Error::<T>::TokenNotFound1866        );1867        let item = <NftItemList<T>>::get(collection_id, item_id);1868        Self::remove_token_index(collection_id, item_id, &item.owner)?;18691870        // update balance1871        let new_balance = <Balance<T>>::get(collection_id, &item.owner)1872            .checked_sub(1)1873            .ok_or(Error::<T>::NumOverflow)?;1874        <Balance<T>>::insert(collection_id, item.owner.clone(), new_balance);1875        <NftItemList<T>>::remove(collection_id, item_id);1876        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);18771878        Ok(())1879    }18801881    fn burn_fungible_item(owner: &T::AccountId, collection: &Collection<T>, value: u128) -> DispatchResult {1882        let collection_id = collection.id;18831884        ensure!(1885            <FungibleItemList<T>>::contains_key(collection_id, owner),1886            Error::<T>::TokenNotFound1887        );1888        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);1889        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);18901891        // update balance1892        let new_balance = <Balance<T>>::get(collection_id, owner)1893            .checked_sub(value)1894            .ok_or(Error::<T>::NumOverflow)?;1895        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);18961897        if balance.value - value > 0 {1898            balance.value -= value;1899            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);1900        }1901        else {1902            <FungibleItemList<T>>::remove(collection_id, owner);1903        }19041905        Ok(())1906    }19071908    pub fn get_collection(collection_id: CollectionId) -> Result<Collection<T>, sp_runtime::DispatchError> {1909        Ok(<CollectionById<T>>::get(collection_id)1910            .ok_or(Error::<T>::CollectionNotFound)?)1911    }19121913    fn save_collection(collection: Collection<T>) {1914        <CollectionById<T>>::insert(collection.id, collection);1915    }19161917    fn check_owner_permissions(target_collection: &Collection<T>, subject: T::AccountId) -> DispatchResult {1918        ensure!(1919            subject == target_collection.owner,1920            Error::<T>::NoPermission1921        );19221923        Ok(())1924    }19251926    fn is_owner_or_admin_permissions(collection: &Collection<T>, subject: T::AccountId) -> bool {1927        let mut result: bool = subject == collection.owner;1928        let exists = <AdminList<T>>::contains_key(collection.id);19291930        if !result & exists {1931            if <AdminList<T>>::get(collection.id).contains(&subject) {1932                result = true1933            }1934        }19351936        result1937    }19381939    fn check_owner_or_admin_permissions(1940        collection: &Collection<T>,1941        subject: T::AccountId,1942    ) -> DispatchResult {1943        let result = Self::is_owner_or_admin_permissions(collection, subject.clone());19441945        ensure!(1946            result,1947            Error::<T>::NoPermission1948        );1949        Ok(())1950    }19511952    fn owned_amount(1953        subject: T::AccountId,1954        target_collection: &Collection<T>,1955        item_id: TokenId,1956    ) -> Option<u128> {1957        let collection_id = target_collection.id;19581959        match target_collection.mode {1960            CollectionMode::NFT => {1961                if <NftItemList<T>>::get(collection_id, item_id).owner == subject {1962                    return Some(1)1963                }1964                None1965            },1966            CollectionMode::Fungible(_) => {1967                if <FungibleItemList<T>>::contains_key(collection_id, &subject) {1968                    return Some(<FungibleItemList<T>>::get(collection_id, &subject)1969                        .value);1970                }1971                None1972            },1973            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)1974                .owner1975                .iter()1976                .find(|i| i.owner == subject)1977                .map(|i| i.fraction),1978            CollectionMode::Invalid => None,1979        }1980    }19811982    fn is_item_owner(subject: T::AccountId, target_collection: &Collection<T>, item_id: TokenId) -> bool {1983        let collection_id = target_collection.id;19841985        match target_collection.mode {1986            CollectionMode::NFT => {1987                <NftItemList<T>>::get(collection_id, item_id).owner == subject1988            }1989            CollectionMode::Fungible(_) => {1990                <FungibleItemList<T>>::contains_key(collection_id, &subject)1991            }1992            CollectionMode::ReFungible => {1993                <ReFungibleItemList<T>>::get(collection_id, item_id)1994                    .owner1995                    .iter()1996                    .any(|i| i.owner == subject)1997            }1998            CollectionMode::Invalid => false,1999        }2000    }20012002    fn check_white_list(collection: &Collection<T>, address: &T::AccountId) -> DispatchResult {2003        let collection_id = collection.id;20042005        let mes = Error::<T>::AddresNotInWhiteList;2006        ensure!(<WhiteList<T>>::contains_key(collection_id, address), mes);20072008        Ok(())2009    }20102011    /// Check if token exists. In case of Fungible, check if there is an entry for 2012    /// the owner in fungible balances double map2013    fn token_exists(2014        target_collection: &Collection<T>,2015        item_id: TokenId,2016        owner: &T::AccountId2017    ) -> DispatchResult {2018        let collection_id = target_collection.id;2019        let exists = match target_collection.mode2020        {2021            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2022            CollectionMode::Fungible(_)  => <FungibleItemList<T>>::contains_key(collection_id, owner),2023            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2024            _ => false2025        };20262027        ensure!(exists == true, Error::<T>::TokenNotFound);2028        Ok(())2029    }20302031    fn transfer_fungible(2032        collection: &Collection<T>,2033        value: u128,2034        owner: &T::AccountId,2035        recipient: &T::AccountId,2036    ) -> DispatchResult {2037        let collection_id = collection.id;2038        Self::token_exists(&collection, 0, owner)?;20392040        let mut balance = <FungibleItemList<T>>::get(collection_id, owner);2041        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);20422043        // Send balance to recipient (updates balanceOf of recipient)2044        Self::add_fungible_item(collection, recipient, value)?;20452046        // update balanceOf of sender2047        <Balance<T>>::insert(collection_id, (*owner).clone(), balance.value - value);20482049        // Reduce or remove sender2050        if balance.value == value {2051            <FungibleItemList<T>>::remove(collection_id, owner);2052        }2053        else {2054            balance.value -= value;2055            <FungibleItemList<T>>::insert(collection_id, (*owner).clone(), balance);2056        }20572058        Ok(())2059    }20602061    fn transfer_refungible(2062        collection: &Collection<T>,2063        item_id: TokenId,2064        value: u128,2065        owner: T::AccountId,2066        new_owner: T::AccountId,2067    ) -> DispatchResult {2068        let collection_id = collection.id;2069        Self::token_exists(collection, item_id, &owner)?;20702071        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id);2072        let item = full_item2073            .owner2074            .iter()2075            .filter(|i| i.owner == owner)2076            .next()2077            .ok_or(Error::<T>::NumOverflow)?;2078        let amount = item.fraction;20792080        ensure!(amount >= value, Error::<T>::TokenValueTooLow);20812082        // update balance2083        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2084            .checked_sub(value)2085            .ok_or(Error::<T>::NumOverflow)?;2086        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);20872088        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2089            .checked_add(value)2090            .ok_or(Error::<T>::NumOverflow)?;2091        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);20922093        let old_owner = item.owner.clone();2094        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);20952096        // transfer2097        if amount == value && !new_owner_has_account {2098            // change owner2099            // new owner do not have account2100            let mut new_full_item = full_item.clone();2101            new_full_item2102                .owner2103                .iter_mut()2104                .find(|i| i.owner == owner)2105                .unwrap()2106                .owner = new_owner.clone();2107            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);21082109            // update index collection2110            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2111        } else {2112            let mut new_full_item = full_item.clone();2113            new_full_item2114                .owner2115                .iter_mut()2116                .find(|i| i.owner == owner)2117                .unwrap()2118                .fraction -= value;21192120            // separate amount2121            if new_owner_has_account {2122                // new owner has account2123                new_full_item2124                    .owner2125                    .iter_mut()2126                    .find(|i| i.owner == new_owner)2127                    .unwrap()2128                    .fraction += value;2129            } else {2130                // new owner do not have account2131                new_full_item.owner.push(Ownership {2132                    owner: new_owner.clone(),2133                    fraction: value,2134                });2135                Self::add_token_index(collection_id, item_id, &new_owner)?;2136            }21372138            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2139        }21402141        Ok(())2142    }21432144    fn transfer_nft(2145        collection: &Collection<T>,2146        item_id: TokenId,2147        sender: T::AccountId,2148        new_owner: T::AccountId,2149    ) -> DispatchResult {2150        let collection_id = collection.id;2151        Self::token_exists(&collection, item_id, &sender)?;21522153        let mut item = <NftItemList<T>>::get(collection_id, item_id);21542155        ensure!(2156            sender == item.owner,2157            Error::<T>::MustBeTokenOwner2158        );21592160        // update balance2161        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.clone())2162            .checked_sub(1)2163            .ok_or(Error::<T>::NumOverflow)?;2164        <Balance<T>>::insert(collection_id, item.owner.clone(), balance_old_owner);21652166        let balancenew_owner = <Balance<T>>::get(collection_id, new_owner.clone())2167            .checked_add(1)2168            .ok_or(Error::<T>::NumOverflow)?;2169        <Balance<T>>::insert(collection_id, new_owner.clone(), balancenew_owner);21702171        // change owner2172        let old_owner = item.owner.clone();2173        item.owner = new_owner.clone();2174        <NftItemList<T>>::insert(collection_id, item_id, item);21752176        // update index collection2177        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;21782179        Ok(())2180    }2181    2182    fn set_re_fungible_variable_data(2183        collection: &Collection<T>,2184        item_id: TokenId,2185        data: Vec<u8>2186    ) -> DispatchResult {2187        let collection_id = collection.id;2188        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id);21892190        item.variable_data = data;21912192        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);21932194        Ok(())2195    }21962197    fn set_nft_variable_data(2198        collection: &Collection<T>,2199        item_id: TokenId,2200        data: Vec<u8>2201    ) -> DispatchResult {2202        let collection_id = collection.id;2203        let mut item = <NftItemList<T>>::get(collection_id, item_id);2204        2205        item.variable_data = data;22062207        <NftItemList<T>>::insert(collection_id, item_id, item);2208        2209        Ok(())2210    }22112212    fn init_collection(item: &Collection<T>) {2213        // check params2214        assert!(2215            item.decimal_points <= MAX_DECIMAL_POINTS,2216            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2217        );2218        assert!(2219            item.name.len() <= 64,2220            "Collection name can not be longer than 63 char"2221        );2222        assert!(2223            item.name.len() <= 256,2224            "Collection description can not be longer than 255 char"2225        );2226        assert!(2227            item.token_prefix.len() <= 16,2228            "Token prefix can not be longer than 15 char"2229        );22302231        // Generate next collection ID2232        let next_id = CreatedCollectionCount::get()2233            .checked_add(1)2234            .unwrap();22352236        CreatedCollectionCount::put(next_id);2237    }22382239    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::AccountId>) {2240        let current_index = <ItemListIndex>::get(collection_id)2241            .checked_add(1)2242            .unwrap();22432244        let item_owner = item.owner.clone();2245        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();22462247        <ItemListIndex>::insert(collection_id, current_index);22482249        // Update balance2250        let new_balance = <Balance<T>>::get(collection_id, &item_owner)2251            .checked_add(1)2252            .unwrap();2253        <Balance<T>>::insert(collection_id, item_owner.clone(), new_balance);2254    }22552256    fn init_fungible_token(collection_id: CollectionId, owner: &T::AccountId, item: &FungibleItemType) {2257        let current_index = <ItemListIndex>::get(collection_id)2258            .checked_add(1)2259            .unwrap();22602261        Self::add_token_index(collection_id, current_index, owner).unwrap();22622263        <ItemListIndex>::insert(collection_id, current_index);22642265        // Update balance2266        let new_balance = <Balance<T>>::get(collection_id, owner)2267            .checked_add(item.value)2268            .unwrap();2269        <Balance<T>>::insert(collection_id, (*owner).clone(), new_balance);2270    }22712272    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::AccountId>) {2273        let current_index = <ItemListIndex>::get(collection_id)2274            .checked_add(1)2275            .unwrap();22762277        let value = item.owner.first().unwrap().fraction;2278        let owner = item.owner.first().unwrap().owner.clone();22792280        Self::add_token_index(collection_id, current_index, &owner).unwrap();22812282        <ItemListIndex>::insert(collection_id, current_index);22832284        // Update balance2285        let new_balance = <Balance<T>>::get(collection_id, &owner)2286            .checked_add(value)2287            .unwrap();2288        <Balance<T>>::insert(collection_id, owner.clone(), new_balance);2289    }22902291    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::AccountId) -> DispatchResult {2292        // add to account limit2293        if <AccountItemCount<T>>::contains_key(owner) {22942295            // bound Owned tokens by a single address2296            let count = <AccountItemCount<T>>::get(owner);2297            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);22982299            <AccountItemCount<T>>::insert(owner.clone(), count2300                .checked_add(1)2301                .ok_or(Error::<T>::NumOverflow)?);2302        }2303        else {2304            <AccountItemCount<T>>::insert(owner.clone(), 1);2305        }23062307        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2308        if list_exists {2309            let mut list = <AddressTokens<T>>::get(collection_id, owner);2310            let item_contains = list.contains(&item_index.clone());23112312            if !item_contains {2313                list.push(item_index.clone());2314            }23152316            <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2317        } else {2318            let mut itm = Vec::new();2319            itm.push(item_index.clone());2320            <AddressTokens<T>>::insert(collection_id, owner.clone(), itm);2321        }23222323        Ok(())2324    }23252326    fn remove_token_index(2327        collection_id: CollectionId,2328        item_index: TokenId,2329        owner: &T::AccountId,2330    ) -> DispatchResult {23312332        // update counter2333        <AccountItemCount<T>>::insert(owner.clone(), 2334            <AccountItemCount<T>>::get(owner)2335            .checked_sub(1)2336            .ok_or(Error::<T>::NumOverflow)?);233723382339        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner);2340        if list_exists {2341            let mut list = <AddressTokens<T>>::get(collection_id, owner);2342            let item_contains = list.contains(&item_index.clone());23432344            if item_contains {2345                list.retain(|&item| item != item_index);2346                <AddressTokens<T>>::insert(collection_id, owner.clone(), list);2347            }2348        }23492350        Ok(())2351    }23522353    fn move_token_index(2354        collection_id: CollectionId,2355        item_index: TokenId,2356        old_owner: &T::AccountId,2357        new_owner: &T::AccountId,2358    ) -> DispatchResult {2359        Self::remove_token_index(collection_id, item_index, old_owner)?;2360        Self::add_token_index(collection_id, item_index, new_owner)?;23612362        Ok(())2363    }2364    2365    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2366        if <ContractOwner<T>>::contains_key(contract.clone()) {2367            let owner = <ContractOwner<T>>::get(contract);2368            ensure!(account == owner, Error::<T>::NoPermission);2369        } else {2370            fail!(Error::<T>::NoPermission);2371        }23722373        Ok(())2374    }2375}23762377////////////////////////////////////////////////////////////////////////////////////////////////////2378// Economic models2379// #region23802381/// Fee multiplier.2382pub type Multiplier = FixedU128;23832384type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;23852386/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2387/// in the queue.2388#[derive(Encode, Decode, Clone, Eq, PartialEq)]2389pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);23902391impl<T: Config + Send + Sync> sp_std::fmt::Debug 2392    for ChargeTransactionPayment<T>2393{2394	#[cfg(feature = "std")]2395	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2396		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2397	}2398	#[cfg(not(feature = "std"))]2399	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2400		Ok(())2401	}2402}24032404impl<T: Config> ChargeTransactionPayment<T>2405where2406    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2407    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2408    T::AccountId: AsRef<[u8]>,2409    T::AccountId: UncheckedFrom<T::Hash>,2410{2411    fn traditional_fee(2412        len: usize,2413        info: &DispatchInfoOf<T::Call>,2414        tip: BalanceOf<T>,2415    ) -> BalanceOf<T>2416    where2417        T::Call: Dispatchable<Info = DispatchInfo>,2418    {2419        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2420    }24212422	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2423        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2424        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2425        let len_saturation = max_block_length as u64 / (len as u64).max(1);2426        let coefficient: BalanceOf<T> = weight_saturation2427            .min(len_saturation)2428            .saturated_into::<BalanceOf<T>>();2429        final_fee2430            .saturating_mul(coefficient)2431            .saturated_into::<TransactionPriority>()2432    }24332434    fn withdraw_fee(2435        &self,2436        who: &T::AccountId,2437        call: &T::Call,2438        info: &DispatchInfoOf<T::Call>,2439        len: usize,2440	) -> Result<2441		(2442			BalanceOf<T>,2443			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2444		),2445		TransactionValidityError,2446	> {2447        let tip = self.0;24482449        // Set fee based on call type. Creating collection costs 1 Unique.2450        // All other transactions have traditional fees so far2451        // let fee = match call.is_sub_type() {2452        //     Some(Call::create_collection(..)) => <BalanceOf<T>>::from(1_000_000_000),2453        //     _ => Self::traditional_fee(len, info, tip), // Flat fee model, use only for testing purposes2454        //                                                 // _ => <BalanceOf<T>>::from(100)2455        // };2456        let fee = Self::traditional_fee(len, info, tip);24572458        // Only mess with balances if fee is not zero.2459        if fee.is_zero() {2460            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2461			.map(|i| (fee, i));2462        }24632464        // Determine who is paying transaction fee based on ecnomic model2465        // Parse call to extract collection ID and access collection sponsor2466        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2467            Some(Call::create_item(collection_id, _owner, _properties)) => {2468                let collection = <CollectionById<T>>::get(collection_id)?;24692470                // sponsor timeout2471                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;24722473                let limit = collection.limits.sponsor_transfer_timeout;2474                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2475                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2476                    let limit_time = last_tx_block + limit.into();2477                    if block_number <= limit_time {2478                        return None;2479                    }2480                }2481                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);24822483                // check free create limit2484                if (collection.limits.sponsored_data_size >= (_properties.len() as u32)) &&2485                   (collection.sponsor_confirmed)2486                {2487                    Some(collection.sponsor)2488                } else {2489                    None2490                }2491            }2492            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2493                let collection = <CollectionById<T>>::get(collection_id)?;2494                2495                let mut sponsor_transfer = false;2496                if collection.sponsor_confirmed {24972498                    let collection_limits = collection.limits;2499                    let collection_mode = collection.mode;2500    2501                    // sponsor timeout2502                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2503                    sponsor_transfer = match collection_mode {2504                        CollectionMode::NFT => {2505    2506                            // get correct limit2507                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2508                                collection_limits.sponsor_transfer_timeout2509                            } else {2510                                ChainLimit::get().nft_sponsor_transfer_timeout2511                            };2512    2513                            let mut sponsored = true;2514                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2515                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2516                                let limit_time = last_tx_block + limit.into();2517                                if block_number <= limit_time {2518                                    sponsored = false;2519                                }2520                            }2521                            if sponsored {2522                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2523                            }25242525                            sponsored2526                        }2527                        CollectionMode::Fungible(_) => {2528    2529                            // get correct limit2530                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2531                                collection_limits.sponsor_transfer_timeout2532                            } else {2533                                ChainLimit::get().fungible_sponsor_transfer_timeout2534                            };2535    2536                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2537                            let mut sponsored = true;2538                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2539                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2540                                let limit_time = last_tx_block + limit.into();2541                                if block_number <= limit_time {2542                                    sponsored = false;2543                                }2544                            }2545                            if sponsored {2546                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2547                            }25482549                            sponsored2550                        }2551                        CollectionMode::ReFungible => {2552    2553                            // get correct limit2554                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2555                                collection_limits.sponsor_transfer_timeout2556                            } else {2557                                ChainLimit::get().refungible_sponsor_transfer_timeout2558                            };2559    2560                            let mut sponsored = true;2561                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2562                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2563                                let limit_time = last_tx_block + limit.into();2564                                if block_number <= limit_time {2565                                    sponsored = false;2566                                }2567                            }2568                            if sponsored {2569                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2570                            }25712572                            sponsored2573                        }2574                        _ => {2575                            false2576                        },2577                    };2578                }25792580                if !sponsor_transfer {2581                    None2582                } else {2583                    Some(collection.sponsor)2584                }2585            }25862587            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2588                let mut sponsor_metadata_changes = false;25892590                let collection = <CollectionById<T>>::get(collection_id)?;25912592                if2593                    collection.sponsor_confirmed &&2594                    // Can't sponsor fungible collection, this tx will be rejected2595                    // as invalid2596                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2597                    data.len() <= collection.limits.sponsored_data_size as usize2598                {2599                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2600                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;26012602                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2603                            .map(|last_block| block_number - last_block > rate_limit)2604                            .unwrap_or(true) 2605                        {2606                            sponsor_metadata_changes = true;2607                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2608                        }2609                    }2610                }26112612                if !sponsor_metadata_changes {2613                    None2614                } else {2615                    Some(collection.sponsor)2616                }2617            }26182619            _ => None,2620        })();26212622        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2623            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26242625                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26262627                let owned_contract = <ContractOwner<T>>::contains_key(called_contract.clone())2628                  && <ContractOwner<T>>::get(called_contract.clone()) == *who;2629                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone()) && <ContractWhiteListEnabled<T>>::get(called_contract.clone());2630                  2631                if !owned_contract && white_list_enabled {2632                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2633                        return Err(InvalidTransaction::Call.into());2634                    }2635                }2636            },2637            _ => {},2638        }26392640        // Sponsor smart contracts2641        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {26422643            // On instantiation: set the contract owner2644            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {26452646                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2647                    &who,2648                    code_hash,2649                    salt,2650                );2651                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26522653                None2654            },26552656            // On instantiation with code: set the contract owner2657            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {26582659                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2660                    &who,2661                    &T::Hashing::hash(&_code),2662                    _salt,2663                );26642665                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());26662667                T::AccountId::default()2668            }26692670            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2671            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {26722673                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());26742675                let mut sponsor_transfer = false;2676                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2677                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2678                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2679                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2680                    let limit_time = last_tx_block + rate_limit;26812682                    if block_number >= limit_time {2683                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2684                        sponsor_transfer = true;2685                    }2686                } else {2687                    sponsor_transfer = false;2688                }2689               2690                if sponsor_transfer {2691                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2692                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2693                            return Some(called_contract);2694                        }2695                    }2696                }26972698                None2699            },27002701            _ => None,2702        });27032704        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());27052706		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2707			.map(|i| (fee, i))2708    }2709}271027112712impl<T: Config + Send + Sync> SignedExtension2713    for ChargeTransactionPayment<T>2714where2715    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2716    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2717    T::AccountId: AsRef<[u8]>,2718    T::AccountId: UncheckedFrom<T::Hash>,2719{2720    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2721    type AccountId = T::AccountId;2722    type Call = T::Call;2723    type AdditionalSigned = ();2724    type Pre = (2725        // tip2726        BalanceOf<T>,2727        // who pays fee2728        Self::AccountId,2729		// imbalance resulting from withdrawing the fee2730		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2731    );2732    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {2733        Ok(())2734    }27352736    fn validate(2737        &self,2738        who: &Self::AccountId,2739        call: &Self::Call,2740        info: &DispatchInfoOf<Self::Call>,2741        len: usize,2742    ) -> TransactionValidity {2743		let (fee, _) = self.withdraw_fee(who, call, info, len)?;2744		Ok(ValidTransaction {2745			priority: Self::get_priority(len, info, fee),2746			..Default::default()2747		})2748    }27492750    fn pre_dispatch(2751        self,2752        who: &Self::AccountId,2753        call: &Self::Call,2754        info: &DispatchInfoOf<Self::Call>,2755        len: usize,2756    ) -> Result<Self::Pre, TransactionValidityError> {2757        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;2758        Ok((self.0, who.clone(), imbalance))2759    }27602761    fn post_dispatch(2762        pre: Self::Pre,2763        info: &DispatchInfoOf<Self::Call>,2764        post_info: &PostDispatchInfoOf<Self::Call>,2765        len: usize,2766        _result: &DispatchResult,2767    ) -> Result<(), TransactionValidityError> {2768		let (tip, who, imbalance) = pre;2769		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(2770			len as u32,2771			info,2772			post_info,2773			tip,2774		);2775		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;2776		Ok(())2777    }2778}27792780// #endregion