git.delta.rocks / unique-network / refs/commits / b6f730ecff68

difftreelog

source

pallets/nft/src/lib.rs112.6 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910extern crate alloc;1112pub use serde::{Serialize, Deserialize};1314use core::ops::{Deref, DerefMut};15use core::cell::RefCell;16use 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, WithdrawReasons,24    },25    weights::{26        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},27        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,28        WeightToFeePolynomial, DispatchClass,29    },30    StorageValue,31    transactional,32};3334use frame_system::{self as system, ensure_signed, ensure_root};35use sp_core::H160;36use sp_runtime::sp_std::prelude::Vec;37use sp_runtime::{38    traits::{39        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,40    },41    transaction_validity::{42        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,43    },44    FixedPointOperand, FixedU128,45};46use sp_runtime::traits::StaticLookup;47use pallet_contracts::chain_extension::UncheckedFrom;48use pallet_ethereum::EthereumTransactionSender;49use pallet_transaction_payment::OnChargeTransaction;5051#[cfg(test)]52mod mock;5354#[cfg(test)]55mod tests;5657mod default_weights;58mod eth;5960pub use eth::NftErcSupport;61pub use eth::account::*;62use eth::erc::{ERC20Events, ERC721Events};6364pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;65pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;66pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;67pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6869// Structs70// #region7172pub type CollectionId = u32;73pub type TokenId = u32;74pub type DecimalPoints = u8;7576#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]77#[derive(Serialize, Deserialize)]78pub enum CollectionMode {79    Invalid,80    NFT,81    // decimal points82    Fungible(DecimalPoints),83    ReFungible,84}8586impl Default for CollectionMode {87    fn default() -> Self {88        Self::Invalid89    }90}9192impl Into<u8> for CollectionMode {93    fn into(self) -> u8 {94        match self {95            CollectionMode::Invalid => 0,96            CollectionMode::NFT => 1,97            CollectionMode::Fungible(_) => 2,98            CollectionMode::ReFungible => 3,99        }100    }101}102103#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]104#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]105pub enum AccessMode {106    Normal,107    WhiteList,108}109impl Default for AccessMode {110    fn default() -> Self {111        Self::Normal112    }113}114115#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]116#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]117pub enum SchemaVersion {118    ImageURL,119    Unique,120}121impl Default for SchemaVersion {122    fn default() -> Self {123        Self::ImageURL124    }125}126127#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]128#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]129pub struct Ownership<AccountId> {130    pub owner: AccountId,131    pub fraction: u128,132}133134#[derive(Encode, Decode, Debug, Clone, PartialEq)]135#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]136pub enum SponsorshipState<AccountId> {137    /// The fees are applied to the transaction sender138    Disabled,139    Unconfirmed(AccountId),140    /// Transactions are sponsored by specified account141    Confirmed(AccountId),142}143144impl<AccountId> SponsorshipState<AccountId> {145    fn sponsor(&self) -> Option<&AccountId> {146        match self {147            Self::Confirmed(sponsor) => Some(sponsor),148            _ => None,149        }150    }151152    fn pending_sponsor(&self) -> Option<&AccountId> {153        match self {154            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),155            _ => None,156        }157    }158159    fn confirmed(&self) -> bool {160        matches!(self, Self::Confirmed(_))161    }162}163164impl<T> Default for SponsorshipState<T> {165    fn default() -> Self {166        Self::Disabled167    }168}169170#[derive(Encode, Decode, Clone, PartialEq)]171#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]172pub struct Collection<T: Config> {173    pub owner: T::AccountId,174    pub mode: CollectionMode,175    pub access: AccessMode,176    pub decimal_points: DecimalPoints,177    pub name: Vec<u16>,        // 64 include null escape char178    pub description: Vec<u16>, // 256 include null escape char179    pub token_prefix: Vec<u8>, // 16 include null escape char180    pub mint_mode: bool,181    pub offchain_schema: Vec<u8>,182    pub schema_version: SchemaVersion,183    pub sponsorship: SponsorshipState<T::AccountId>,184    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 185    pub variable_on_chain_schema: Vec<u8>, //186    pub const_on_chain_schema: Vec<u8>, //187}188189pub struct CollectionHandle<T: Config> {190    pub id: CollectionId,191    collection: Collection<T>,192    logs: eth::log::LogRecorder,193    evm_address: H160,194    gas_limit: RefCell<u64>,195}196impl<T: Config> CollectionHandle<T> {197	pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {198		<CollectionById<T>>::get(id)199			.map(|collection| Self {200				id,201				collection,202                logs: eth::log::LogRecorder::default(),203                evm_address: eth::collection_id_to_address(id),204                gas_limit: RefCell::new(gas_limit),205			})206	}207    pub fn get(id: CollectionId) -> Option<Self> {208        Self::get_with_gas_limit(id, u64::MAX)209    }210    pub fn gas_left(&self) -> u64 {211        *self.gas_limit.borrow()212    }213    pub fn consume_gas(&self, gas: u64) -> DispatchResult {214        let mut gas_limit = self.gas_limit.borrow_mut();215        if *gas_limit < gas {216            fail!(Error::<T>::OutOfGas);217        }218        *gas_limit -= gas;219        Ok(())220    }221    pub fn log(&self, log: impl evm_coder::ToLog) {222        self.logs.log(log.to_log(self.evm_address))223    }224    pub fn into_inner(self) -> Collection<T> {225        self.collection.clone()226    }227}228229impl<T: Config> Deref for CollectionHandle<T> {230    type Target = Collection<T>;231232    fn deref(&self) -> &Self::Target {233        &self.collection234    }235}236237impl<T: Config> DerefMut for CollectionHandle<T> {238    fn deref_mut(&mut self) -> &mut Self::Target {239        &mut self.collection240    }241}242243#[derive(Encode, Decode, Debug, Clone, PartialEq)]244#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]245pub struct NftItemType<AccountId> {246    pub owner: AccountId,247    pub const_data: Vec<u8>,248    pub variable_data: Vec<u8>,249}250251#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]252#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]253pub struct FungibleItemType {254    pub value: u128,255}256257#[derive(Encode, Decode, Debug, Clone, PartialEq)]258#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]259pub struct ReFungibleItemType<AccountId> {260    pub owner: Vec<Ownership<AccountId>>,261    pub const_data: Vec<u8>,262    pub variable_data: Vec<u8>,263}264265// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]266// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]267// pub struct VestingItem<AccountId, Moment> {268//     pub sender: AccountId,269//     pub recipient: AccountId,270//     pub collection_id: CollectionId,271//     pub item_id: TokenId,272//     pub amount: u64,273//     pub vesting_date: Moment,274// }275276#[derive(Encode, Decode, Debug, Clone, PartialEq)]277#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]278pub struct CollectionLimits<BlockNumber: Encode + Decode> {279    pub account_token_ownership_limit: u32,280    pub sponsored_data_size: u32,281    /// None - setVariableMetadata is not sponsored282    /// Some(v) - setVariableMetadata is sponsored 283    ///           if there is v block between txs284    pub sponsored_data_rate_limit: Option<BlockNumber>,285    pub token_limit: u32,286287    // Timeouts for item types in passed blocks288    pub sponsor_transfer_timeout: u32,289    pub owner_can_transfer: bool,290    pub owner_can_destroy: bool,291}292293impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {294    fn default() -> Self {295        Self { 296            account_token_ownership_limit: 10_000_000, 297            token_limit: u32::max_value(),298            sponsored_data_size: u32::MAX, 299            sponsored_data_rate_limit: None,300            sponsor_transfer_timeout: 14400,301            owner_can_transfer: true,302            owner_can_destroy: true303        }304    }305}306307#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]308#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]309pub struct ChainLimits {310    pub collection_numbers_limit: u32,311    pub account_token_ownership_limit: u32,312    pub collections_admins_limit: u64,313    pub custom_data_limit: u32,314315    // Timeouts for item types in passed blocks316    pub nft_sponsor_transfer_timeout: u32,317    pub fungible_sponsor_transfer_timeout: u32,318    pub refungible_sponsor_transfer_timeout: u32,319320    // Schema limits321    pub offchain_schema_limit: u32,322    pub variable_on_chain_schema_limit: u32,323    pub const_on_chain_schema_limit: u32,324}325326pub trait WeightInfo {327	fn create_collection() -> Weight;328	fn destroy_collection() -> Weight;329	fn add_to_white_list() -> Weight;330	fn remove_from_white_list() -> Weight;331    fn set_public_access_mode() -> Weight;332    fn set_mint_permission() -> Weight;333    fn change_collection_owner() -> Weight;334    fn add_collection_admin() -> Weight;335    fn remove_collection_admin() -> Weight;336    fn set_collection_sponsor() -> Weight;337    fn confirm_sponsorship() -> Weight;338    fn remove_collection_sponsor() -> Weight;339    fn create_item(s: usize) -> Weight;340    fn burn_item() -> Weight;341    fn transfer() -> Weight;342    fn approve() -> Weight;343    fn transfer_from() -> Weight;344    fn set_offchain_schema() -> Weight;345    fn set_const_on_chain_schema() -> Weight;346    fn set_variable_on_chain_schema() -> Weight;347    fn set_variable_meta_data() -> Weight;348    fn enable_contract_sponsoring() -> Weight;349    fn set_schema_version() -> Weight;350    fn set_chain_limits() -> Weight;351    fn set_contract_sponsoring_rate_limit() -> Weight;352    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;353    fn toggle_contract_white_list() -> Weight;354    fn add_to_contract_white_list() -> Weight;355    fn remove_from_contract_white_list() -> Weight;356    fn set_collection_limits() -> Weight;357}358359#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub struct CreateNftData {362    pub const_data: Vec<u8>,363    pub variable_data: Vec<u8>,364}365366#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]367#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]368pub struct CreateFungibleData {369    pub value: u128,370}371372#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]373#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]374pub struct CreateReFungibleData {375    pub const_data: Vec<u8>,376    pub variable_data: Vec<u8>,377    pub pieces: u128,378}379380#[derive(Encode, Decode, Debug, Clone, PartialEq)]381#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]382pub enum CreateItemData {383    NFT(CreateNftData),384    Fungible(CreateFungibleData),385    ReFungible(CreateReFungibleData),386}387388impl CreateItemData {389    pub fn len(&self) -> usize {390        let len = match self {391            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),392            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),393            _ => 0394        };395        396        return len;397    }398}399400impl From<CreateNftData> for CreateItemData {401    fn from(item: CreateNftData) -> Self {402        CreateItemData::NFT(item)403    }404}405406impl From<CreateReFungibleData> for CreateItemData {407    fn from(item: CreateReFungibleData) -> Self {408        CreateItemData::ReFungible(item)409    }410}411412impl From<CreateFungibleData> for CreateItemData {413    fn from(item: CreateFungibleData) -> Self {414        CreateItemData::Fungible(item)415    }416}417418419decl_error! {420	/// Error for non-fungible-token module.421	pub enum Error for Module<T: Config> {422        /// Total collections bound exceeded.423        TotalCollectionsLimitExceeded,424		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.425        CollectionDecimalPointLimitExceeded, 426        /// Collection name can not be longer than 63 char.427        CollectionNameLimitExceeded, 428        /// Collection description can not be longer than 255 char.429        CollectionDescriptionLimitExceeded, 430        /// Token prefix can not be longer than 15 char.431        CollectionTokenPrefixLimitExceeded,432        /// This collection does not exist.433        CollectionNotFound,434        /// Item not exists.435        TokenNotFound,436        /// Admin not found437        AdminNotFound,438        /// Arithmetic calculation overflow.439        NumOverflow,       440        /// Account already has admin role.441        AlreadyAdmin,  442        /// You do not own this collection.443        NoPermission,444        /// This address is not set as sponsor, use setCollectionSponsor first.445        ConfirmUnsetSponsorFail,446        /// Collection is not in mint mode.447        PublicMintingNotAllowed,448        /// Sender parameter and item owner must be equal.449        MustBeTokenOwner,450        /// Item balance not enough.451        TokenValueTooLow,452        /// Size of item is too large.453        NftSizeLimitExceeded,454        /// No approve found455        ApproveNotFound,456        /// Requested value more than approved.457        TokenValueNotEnough,458        /// Only approved addresses can call this method.459        ApproveRequired,460        /// Address is not in white list.461        AddresNotInWhiteList,462        /// Number of collection admins bound exceeded.463        CollectionAdminsLimitExceeded,464        /// Owned tokens by a single address bound exceeded.465        AddressOwnershipLimitExceeded,466        /// Length of items properties must be greater than 0.467        EmptyArgument,468        /// const_data exceeded data limit.469        TokenConstDataLimitExceeded,470        /// variable_data exceeded data limit.471        TokenVariableDataLimitExceeded,472        /// Not NFT item data used to mint in NFT collection.473        NotNftDataUsedToMintNftCollectionToken,474        /// Not Fungible item data used to mint in Fungible collection.475        NotFungibleDataUsedToMintFungibleCollectionToken,476        /// Not Re Fungible item data used to mint in Re Fungible collection.477        NotReFungibleDataUsedToMintReFungibleCollectionToken,478        /// Unexpected collection type.479        UnexpectedCollectionType,480        /// Can't store metadata in fungible tokens.481        CantStoreMetadataInFungibleTokens,482        /// Collection token limit exceeded483        CollectionTokenLimitExceeded,484        /// Account token limit exceeded per collection485        AccountTokenLimitExceeded,486        /// Collection limit bounds per collection exceeded487        CollectionLimitBoundsExceeded,488        /// Tried to enable permissions which are only permitted to be disabled489        OwnerPermissionsCantBeReverted,490        /// Schema data size limit bound exceeded491        SchemaDataLimitExceeded,492        /// Maximum refungibility exceeded493        WrongRefungiblePieces,494        /// createRefungible should be called with one owner495        BadCreateRefungibleCall,496        /// Gas limit exceeded497        OutOfGas,498	}499}500501pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {502    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;503504    /// Weight information for extrinsics in this pallet.505	type WeightInfo: WeightInfo;506507    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;508    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;509    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;510511	type CrossAccountId: CrossAccountId<Self::AccountId>;512    type Currency: Currency<Self::AccountId>;513    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;514    type TreasuryAccountId: Get<Self::AccountId>;515516    type EthereumChainId: Get<u64>;517    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;518}519520#[cfg(feature = "runtime-benchmarks")]521mod benchmarking;522523// #endregion524525// # Used definitions526//527// ## User control levels528//529// chain-controlled - key is uncontrolled by user530//                    i.e autoincrementing index531//                    can use non-cryptographic hash532// real - key is controlled by user533//        but it is hard to generate enough colliding values, i.e owner of signed txs534//        can use non-cryptographic hash535// controlled - key is completly controlled by users536//              i.e maps with mutable keys537//              should use cryptographic hash538//539// ## User control level downgrade reasons540//541// ?1 - chain-controlled -> controlled542//      collections/tokens can be destroyed, resulting in massive holes543// ?2 - chain-controlled -> controlled544//      same as ?1, but can be only added, resulting in easier exploitation545// ?3 - real -> controlled546//      no confirmation required, so addresses can be easily generated547decl_storage! {548    trait Store for Module<T: Config> as Nft {549550        //#region Private members551        /// Id of next collection552        CreatedCollectionCount: u32;553        /// Used for migrations554        ChainVersion: u64;555        /// Id of last collection token556        /// Collection id (controlled?1)557        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;558        //#endregion559560        //#region Chain limits struct561        pub ChainLimit get(fn chain_limit) config(): ChainLimits;562        //#endregion563564        //#region Bound counters565        /// Amount of collections destroyed, used for total amount tracking with566        /// CreatedCollectionCount567        DestroyedCollectionCount: u32;568        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)569        /// Account id (real)570        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;571        //#endregion572573        //#region Basic collections574        /// Collection info575        /// Collection id (controlled?1)576        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;577        /// List of collection admins578        /// Collection id (controlled?2)579        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;580        /// Whitelisted collection users581        /// Collection id (controlled?2), user id (controlled?3)582        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;583        //#endregion584585        /// How many of collection items user have586        /// Collection id (controlled?2), account id (real)587        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;588589        /// Amount of items which spender can transfer out of owners account (via transferFrom)590        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))591        /// TODO: Off chain worker should remove from this map when token gets removed592        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;593594        //#region Item collections595        /// Collection id (controlled?2), token id (controlled?1)596        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;597        /// Collection id (controlled?2), owner (controlled?2)598        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;599        /// Collection id (controlled?2), token id (controlled?1)600        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;601        //#endregion602603        //#region Index list604        /// Collection id (controlled?2), tokens owner (controlled?2)605        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;606        //#endregion607608        //#region Tokens transfer rate limit baskets609        /// (Collection id (controlled?2), who created (real))610        /// TODO: Off chain worker should remove from this map when collection gets removed611        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;612        /// Collection id (controlled?2), token id (controlled?2)613        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;614        /// Collection id (controlled?2), owning user (real)615        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;616        /// Collection id (controlled?2), token id (controlled?2)617        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;618        //#endregion619620        /// Variable metadata sponsoring621        /// Collection id (controlled?2), token id (controlled?2)622        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;623      624        //#region Contract Sponsorship and Ownership625        /// Contract address (real)626        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;627        /// Contract address (real)628        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;629        /// (Contract address(real), caller (real))630        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;631        /// Contract address (real)632        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;633        /// Contract address (real)634        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 635        /// Contract address (real) => Whitelisted user (controlled?3)636        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 637        //#endregion638    }639    add_extra_genesis {640        build(|config: &GenesisConfig<T>| {641            // Modification of storage642            for (_num, _c) in &config.collection_id {643                <Module<T>>::init_collection(_c);644            }645646            for (_num, _c, _i) in &config.nft_item_id {647                <Module<T>>::init_nft_token(*_c, _i);648            }649650            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {651                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);652            }653654            for (_num, _c, _i) in &config.refungible_item_id {655                <Module<T>>::init_refungible_token(*_c, _i);656            }657        })658    }659}660661decl_event!(662    pub enum Event<T>663    where664        AccountId = <T as frame_system::Config>::AccountId,665        CrossAccountId = <T as Config>::CrossAccountId,666    {667        /// New collection was created668        /// 669        /// # Arguments670        /// 671        /// * collection_id: Globally unique identifier of newly created collection.672        /// 673        /// * mode: [CollectionMode] converted into u8.674        /// 675        /// * account_id: Collection owner.676        CollectionCreated(CollectionId, u8, AccountId),677678        /// New item was created.679        /// 680        /// # Arguments681        /// 682        /// * collection_id: Id of the collection where item was created.683        /// 684        /// * item_id: Id of an item. Unique within the collection.685        ///686        /// * recipient: Owner of newly created item 687        ItemCreated(CollectionId, TokenId, CrossAccountId),688689        /// Collection item was burned.690        /// 691        /// # Arguments692        /// 693        /// collection_id.694        /// 695        /// item_id: Identifier of burned NFT.696        ItemDestroyed(CollectionId, TokenId),697698        /// Item was transferred699        ///700        /// * collection_id: Id of collection to which item is belong701        ///702        /// * item_id: Id of an item703        ///704        /// * sender: Original owner of item705        ///706        /// * recipient: New owner of item707        ///708        /// * amount: Always 1 for NFT709        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),710711        /// * collection_id712        ///713        /// * item_id714        ///715        /// * sender716        ///717        /// * spender718        ///719        /// * amount720        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),721    }722);723724decl_module! {725    pub struct Module<T: Config> for enum Call 726    where 727        origin: T::Origin728    {729        fn deposit_event() = default;730        type Error = Error<T>;731732        fn on_initialize(now: T::BlockNumber) -> Weight {733            0734        }735736        /// 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.737        /// 738        /// # Permissions739        /// 740        /// * Anyone.741        /// 742        /// # Arguments743        /// 744        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.745        /// 746        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.747        /// 748        /// * token_prefix: UTF-8 string with token prefix.749        /// 750        /// * mode: [CollectionMode] collection type and type dependent data.751        // returns collection ID752        #[weight = <T as Config>::WeightInfo::create_collection()]753        #[transactional]754        pub fn create_collection(origin,755                                 collection_name: Vec<u16>,756                                 collection_description: Vec<u16>,757                                 token_prefix: Vec<u8>,758                                 mode: CollectionMode) -> DispatchResult {759760            // Anyone can create a collection761            let who = ensure_signed(origin)?;762763            // Take a (non-refundable) deposit of collection creation764            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();765            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(766                &T::TreasuryAccountId::get(),767                T::CollectionCreationPrice::get(),768            ));769            <T as Config>::Currency::settle(770                &who,771                imbalance,772                WithdrawReasons::TRANSFER,773                ExistenceRequirement::KeepAlive,774            ).map_err(|_| Error::<T>::NoPermission)?;775776            let decimal_points = match mode {777                CollectionMode::Fungible(points) => points,778                _ => 0779            };780781            let chain_limit = ChainLimit::get();782783            let created_count = CreatedCollectionCount::get();784            let destroyed_count = DestroyedCollectionCount::get();785786            // bound Total number of collections787            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);788789            // check params790            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);791            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);792            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);793            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);794795            // Generate next collection ID796            let next_id = created_count797                .checked_add(1)798                .ok_or(Error::<T>::NumOverflow)?;799800            CreatedCollectionCount::put(next_id);801802            let limits = CollectionLimits {803                sponsored_data_size: chain_limit.custom_data_limit,804                ..Default::default()805            };806807            // Create new collection808            let new_collection = Collection {809                owner: who.clone(),810                name: collection_name,811                mode: mode.clone(),812                mint_mode: false,813                access: AccessMode::Normal,814                description: collection_description,815                decimal_points: decimal_points,816                token_prefix: token_prefix,817                offchain_schema: Vec::new(),818                schema_version: SchemaVersion::ImageURL,819                sponsorship: SponsorshipState::Disabled,820                variable_on_chain_schema: Vec::new(),821                const_on_chain_schema: Vec::new(),822                limits,823            };824825            // Add new collection to map826            <CollectionById<T>>::insert(next_id, new_collection);827828            // call event829            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));830831            Ok(())832        }833834        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.835        /// 836        /// # Permissions837        /// 838        /// * Collection Owner.839        /// 840        /// # Arguments841        /// 842        /// * collection_id: collection to destroy.843        #[weight = <T as Config>::WeightInfo::destroy_collection()]844        #[transactional]845        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {846847            let sender = ensure_signed(origin)?;848            let collection = Self::get_collection(collection_id)?;849            Self::check_owner_permissions(&collection, &sender)?;850            if !collection.limits.owner_can_destroy {851                fail!(Error::<T>::NoPermission);852            }853854            <AddressTokens<T>>::remove_prefix(collection_id);855            <Allowances<T>>::remove_prefix(collection_id);856            <Balance<T>>::remove_prefix(collection_id);857            <ItemListIndex>::remove(collection_id);858            <AdminList<T>>::remove(collection_id);859            <CollectionById<T>>::remove(collection_id);860            <WhiteList<T>>::remove_prefix(collection_id);861862            <NftItemList<T>>::remove_prefix(collection_id);863            <FungibleItemList<T>>::remove_prefix(collection_id);864            <ReFungibleItemList<T>>::remove_prefix(collection_id);865866            <NftTransferBasket<T>>::remove_prefix(collection_id);867            <FungibleTransferBasket<T>>::remove_prefix(collection_id);868            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);869870            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);871872            DestroyedCollectionCount::put(DestroyedCollectionCount::get()873                .checked_add(1)874                .ok_or(Error::<T>::NumOverflow)?);875876            Ok(())877        }878879        /// Add an address to white list.880        /// 881        /// # Permissions882        /// 883        /// * Collection Owner884        /// * Collection Admin885        /// 886        /// # Arguments887        /// 888        /// * collection_id.889        /// 890        /// * address.891        #[weight = <T as Config>::WeightInfo::add_to_white_list()]892        #[transactional]893        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{894895            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);896            let collection = Self::get_collection(collection_id)?;897898            Self::toggle_white_list_internal(899                &sender,900                &collection,901                &address,902                true,903            )?;904905            Ok(())906        }907908        /// Remove an address from white list.909        /// 910        /// # Permissions911        /// 912        /// * Collection Owner913        /// * Collection Admin914        /// 915        /// # Arguments916        /// 917        /// * collection_id.918        /// 919        /// * address.920        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]921        #[transactional]922        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{923924            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);925            let collection = Self::get_collection(collection_id)?;926927            Self::toggle_white_list_internal(928                &sender,929                &collection,930                &address,931                false,932            )?;933934            Ok(())935        }936937        /// Toggle between normal and white list access for the methods with access for `Anyone`.938        /// 939        /// # Permissions940        /// 941        /// * Collection Owner.942        /// 943        /// # Arguments944        /// 945        /// * collection_id.946        /// 947        /// * mode: [AccessMode]948        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]949        #[transactional]950        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult951        {952            let sender = ensure_signed(origin)?;953954            let mut target_collection = Self::get_collection(collection_id)?;955            Self::check_owner_permissions(&target_collection, &sender)?;956            target_collection.access = mode;957            Self::save_collection(target_collection);958959            Ok(())960        }961962        /// Allows Anyone to create tokens if:963        /// * White List is enabled, and964        /// * Address is added to white list, and965        /// * This method was called with True parameter966        /// 967        /// # Permissions968        /// * Collection Owner969        ///970        /// # Arguments971        /// 972        /// * collection_id.973        /// 974        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.975        #[weight = <T as Config>::WeightInfo::set_mint_permission()]976        #[transactional]977        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult978        {979            let sender = ensure_signed(origin)?;980981            let mut target_collection = Self::get_collection(collection_id)?;982            Self::check_owner_permissions(&target_collection, &sender)?;983            target_collection.mint_mode = mint_permission;984            Self::save_collection(target_collection);985986            Ok(())987        }988989        /// Change the owner of the collection.990        /// 991        /// # Permissions992        /// 993        /// * Collection Owner.994        /// 995        /// # Arguments996        /// 997        /// * collection_id.998        /// 999        /// * new_owner.1000        #[weight = <T as Config>::WeightInfo::change_collection_owner()]1001        #[transactional]1002        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {10031004            let sender = ensure_signed(origin)?;1005            let mut target_collection = Self::get_collection(collection_id)?;1006            Self::check_owner_permissions(&target_collection, &sender)?;1007            target_collection.owner = new_owner;1008            Self::save_collection(target_collection);10091010            Ok(())1011        }10121013        /// Adds an admin of the Collection.1014        /// 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. 1015        /// 1016        /// # Permissions1017        /// 1018        /// * Collection Owner.1019        /// * Collection Admin.1020        /// 1021        /// # Arguments1022        /// 1023        /// * collection_id: ID of the Collection to add admin for.1024        /// 1025        /// * new_admin_id: Address of new admin to add.1026        #[weight = <T as Config>::WeightInfo::add_collection_admin()]1027        #[transactional]1028        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1029            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1030            let collection = Self::get_collection(collection_id)?;1031            Self::check_owner_or_admin_permissions(&collection, &sender)?;1032            let mut admin_arr = <AdminList<T>>::get(collection_id);10331034            match admin_arr.binary_search(&new_admin_id) {1035                Ok(_) => {},1036                Err(idx) => {1037                    let limits = ChainLimit::get();1038                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1039                    admin_arr.insert(idx, new_admin_id);1040                    <AdminList<T>>::insert(collection_id, admin_arr);1041                }1042            }1043            Ok(())1044        }10451046        /// 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.1047        ///1048        /// # Permissions1049        /// 1050        /// * Collection Owner.1051        /// * Collection Admin.1052        /// 1053        /// # Arguments1054        /// 1055        /// * collection_id: ID of the Collection to remove admin for.1056        /// 1057        /// * account_id: Address of admin to remove.1058        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1059        #[transactional]1060        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1061            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1062            let collection = Self::get_collection(collection_id)?;1063            Self::check_owner_or_admin_permissions(&collection, &sender)?;1064            let mut admin_arr = <AdminList<T>>::get(collection_id);10651066            match admin_arr.binary_search(&account_id) {1067                Ok(idx) => {1068                    admin_arr.remove(idx);1069                    <AdminList<T>>::insert(collection_id, admin_arr);1070                },1071                Err(_) => {}1072            }1073            Ok(())1074        }10751076        /// # Permissions1077        /// 1078        /// * Collection Owner1079        /// 1080        /// # Arguments1081        /// 1082        /// * collection_id.1083        /// 1084        /// * new_sponsor.1085        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1086        #[transactional]1087        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1088            let sender = ensure_signed(origin)?;1089            let mut target_collection = Self::get_collection(collection_id)?;1090            Self::check_owner_permissions(&target_collection, &sender)?;10911092            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1093            Self::save_collection(target_collection);10941095            Ok(())1096        }10971098        /// # Permissions1099        /// 1100        /// * Sponsor.1101        /// 1102        /// # Arguments1103        /// 1104        /// * collection_id.1105        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1106        #[transactional]1107        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1108            let sender = ensure_signed(origin)?;11091110            let mut target_collection = Self::get_collection(collection_id)?;1111            ensure!(1112                target_collection.sponsorship.pending_sponsor() == Some(&sender),1113                Error::<T>::ConfirmUnsetSponsorFail1114            );11151116            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1117            Self::save_collection(target_collection);11181119            Ok(())1120        }11211122        /// Switch back to pay-per-own-transaction model.1123        ///1124        /// # Permissions1125        ///1126        /// * Collection owner.1127        /// 1128        /// # Arguments1129        /// 1130        /// * collection_id.1131        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1132        #[transactional]1133        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1134            let sender = ensure_signed(origin)?;11351136            let mut target_collection = Self::get_collection(collection_id)?;1137            Self::check_owner_permissions(&target_collection, &sender)?;11381139            target_collection.sponsorship = SponsorshipState::Disabled;1140            Self::save_collection(target_collection);11411142            Ok(())1143        }11441145        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1146        /// 1147        /// # Permissions1148        /// 1149        /// * Collection Owner.1150        /// * Collection Admin.1151        /// * Anyone if1152        ///     * White List is enabled, and1153        ///     * Address is added to white list, and1154        ///     * MintPermission is enabled (see SetMintPermission method)1155        /// 1156        /// # Arguments1157        /// 1158        /// * collection_id: ID of the collection.1159        /// 1160        /// * owner: Address, initial owner of the NFT.1161        ///1162        /// * data: Token data to store on chain.1163        // #[weight =1164        // (130_000_000 as Weight)1165        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1166        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1167        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11681169        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1170        #[transactional]1171        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1172            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1173            let collection = Self::get_collection(collection_id)?;11741175            Self::create_item_internal(&sender, &collection, &owner, data)?;11761177            Self::submit_logs(collection)?;1178            Ok(())1179        }11801181        /// This method creates multiple items in a collection created with CreateCollection method.1182        /// 1183        /// # Permissions1184        /// 1185        /// * Collection Owner.1186        /// * Collection Admin.1187        /// * Anyone if1188        ///     * White List is enabled, and1189        ///     * Address is added to white list, and1190        ///     * MintPermission is enabled (see SetMintPermission method)1191        /// 1192        /// # Arguments1193        /// 1194        /// * collection_id: ID of the collection.1195        /// 1196        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1197        /// 1198        /// * owner: Address, initial owner of the NFT.1199        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1200                               .map(|data| { data.len() })1201                               .sum())]1202        #[transactional]1203        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {12041205            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1206            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1207            let collection = Self::get_collection(collection_id)?;12081209            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;12101211            Self::submit_logs(collection)?;1212            Ok(())1213        }12141215        /// Destroys a concrete instance of NFT.1216        /// 1217        /// # Permissions1218        /// 1219        /// * Collection Owner.1220        /// * Collection Admin.1221        /// * Current NFT Owner.1222        /// 1223        /// # Arguments1224        /// 1225        /// * collection_id: ID of the collection.1226        /// 1227        /// * item_id: ID of NFT to burn.1228        #[weight = <T as Config>::WeightInfo::burn_item()]1229        #[transactional]1230        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12311232            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1233            let target_collection = Self::get_collection(collection_id)?;12341235            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12361237            Self::submit_logs(target_collection)?;1238            Ok(())1239        }12401241        /// Change ownership of the token.1242        /// 1243        /// # Permissions1244        /// 1245        /// * Collection Owner1246        /// * Collection Admin1247        /// * Current NFT owner1248        ///1249        /// # Arguments1250        /// 1251        /// * recipient: Address of token recipient.1252        /// 1253        /// * collection_id.1254        /// 1255        /// * item_id: ID of the item1256        ///     * Non-Fungible Mode: Required.1257        ///     * Fungible Mode: Ignored.1258        ///     * Re-Fungible Mode: Required.1259        /// 1260        /// * value: Amount to transfer.1261        ///     * Non-Fungible Mode: Ignored1262        ///     * Fungible Mode: Must specify transferred amount1263        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1264        #[weight = <T as Config>::WeightInfo::transfer()]1265        #[transactional]1266        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1267            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1268            let collection = Self::get_collection(collection_id)?;12691270            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12711272            Self::submit_logs(collection)?;1273            Ok(())1274        }12751276        /// Set, change, or remove approved address to transfer the ownership of the NFT.1277        /// 1278        /// # Permissions1279        /// 1280        /// * Collection Owner1281        /// * Collection Admin1282        /// * Current NFT owner1283        /// 1284        /// # Arguments1285        /// 1286        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1287        /// 1288        /// * collection_id.1289        /// 1290        /// * item_id: ID of the item.1291        #[weight = <T as Config>::WeightInfo::approve()]1292        #[transactional]1293        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1294            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1295            let collection = Self::get_collection(collection_id)?;12961297            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12981299            Self::submit_logs(collection)?;1300            Ok(())1301        }1302        1303        /// 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.1304        /// 1305        /// # Permissions1306        /// * Collection Owner1307        /// * Collection Admin1308        /// * Current NFT owner1309        /// * Address approved by current NFT owner1310        /// 1311        /// # Arguments1312        /// 1313        /// * from: Address that owns token.1314        /// 1315        /// * recipient: Address of token recipient.1316        /// 1317        /// * collection_id.1318        /// 1319        /// * item_id: ID of the item.1320        /// 1321        /// * value: Amount to transfer.1322        #[weight = <T as Config>::WeightInfo::transfer_from()]1323        #[transactional]1324        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1325            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1326            let collection = Self::get_collection(collection_id)?;13271328            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13291330            Self::submit_logs(collection)?;1331            Ok(())1332        }1333        // #[weight = 0]1334        //     // let no_perm_mes = "You do not have permissions to modify this collection";1335        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1336        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1337        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13381339        //     // // on_nft_received  call13401341        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13421343        //     Ok(())1344        // }13451346        /// Set off-chain data schema.1347        /// 1348        /// # Permissions1349        /// 1350        /// * Collection Owner1351        /// * Collection Admin1352        /// 1353        /// # Arguments1354        /// 1355        /// * collection_id.1356        /// 1357        /// * schema: String representing the offchain data schema.1358        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1359        #[transactional]1360        pub fn set_variable_meta_data (1361            origin,1362            collection_id: CollectionId,1363            item_id: TokenId,1364            data: Vec<u8>1365        ) -> DispatchResult {1366            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1367            1368            let collection = Self::get_collection(collection_id)?;13691370            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13711372            Ok(())1373        }1374 1375        /// Set schema standard1376        /// ImageURL1377        /// Unique1378        /// 1379        /// # Permissions1380        /// 1381        /// * Collection Owner1382        /// * Collection Admin1383        /// 1384        /// # Arguments1385        /// 1386        /// * collection_id.1387        /// 1388        /// * schema: SchemaVersion: enum1389        #[weight = <T as Config>::WeightInfo::set_schema_version()]1390        #[transactional]1391        pub fn set_schema_version(1392            origin,1393            collection_id: CollectionId,1394            version: SchemaVersion1395        ) -> DispatchResult {1396            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1397            let mut target_collection = Self::get_collection(collection_id)?;1398            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1399            target_collection.schema_version = version;1400            Self::save_collection(target_collection);14011402            Ok(())1403        }14041405        /// Set off-chain data schema.1406        /// 1407        /// # Permissions1408        /// 1409        /// * Collection Owner1410        /// * Collection Admin1411        /// 1412        /// # Arguments1413        /// 1414        /// * collection_id.1415        /// 1416        /// * schema: String representing the offchain data schema.1417        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1418        #[transactional]1419        pub fn set_offchain_schema(1420            origin,1421            collection_id: CollectionId,1422            schema: Vec<u8>1423        ) -> DispatchResult {1424            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1425            let mut target_collection = Self::get_collection(collection_id)?;1426            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14271428            // check schema limit1429            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14301431            target_collection.offchain_schema = schema;1432            Self::save_collection(target_collection);14331434            Ok(())1435        }14361437        /// Set const on-chain data schema.1438        /// 1439        /// # Permissions1440        /// 1441        /// * Collection Owner1442        /// * Collection Admin1443        /// 1444        /// # Arguments1445        /// 1446        /// * collection_id.1447        /// 1448        /// * schema: String representing the const on-chain data schema.1449        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1450        #[transactional]1451        pub fn set_const_on_chain_schema (1452            origin,1453            collection_id: CollectionId,1454            schema: Vec<u8>1455        ) -> DispatchResult {1456            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1457            let mut target_collection = Self::get_collection(collection_id)?;1458            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14591460            // check schema limit1461            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14621463            target_collection.const_on_chain_schema = schema;1464            Self::save_collection(target_collection);14651466            Ok(())1467        }14681469        /// Set variable on-chain data schema.1470        /// 1471        /// # Permissions1472        /// 1473        /// * Collection Owner1474        /// * Collection Admin1475        /// 1476        /// # Arguments1477        /// 1478        /// * collection_id.1479        /// 1480        /// * schema: String representing the variable on-chain data schema.1481        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1482        #[transactional]1483        pub fn set_variable_on_chain_schema (1484            origin,1485            collection_id: CollectionId,1486            schema: Vec<u8>1487        ) -> DispatchResult {1488            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1489            let mut target_collection = Self::get_collection(collection_id)?;1490            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14911492            // check schema limit1493            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14941495            target_collection.variable_on_chain_schema = schema;1496            Self::save_collection(target_collection);14971498            Ok(())1499        }15001501        // Sudo permissions function1502        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1503        #[transactional]1504        pub fn set_chain_limits(1505            origin,1506            limits: ChainLimits1507        ) -> DispatchResult {15081509            #[cfg(not(feature = "runtime-benchmarks"))]1510            ensure_root(origin)?;15111512            <ChainLimit>::put(limits);1513            Ok(())1514        }15151516        /// Enable smart contract self-sponsoring.1517        /// 1518        /// # Permissions1519        /// 1520        /// * Contract Owner1521        /// 1522        /// # Arguments1523        /// 1524        /// * contract address1525        /// * enable flag1526        /// 1527        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1528        #[transactional]1529        pub fn enable_contract_sponsoring(1530            origin,1531            contract_address: T::AccountId,1532            enable: bool1533        ) -> DispatchResult {15341535            let sender = ensure_signed(origin)?;15361537            #[cfg(feature = "runtime-benchmarks")]1538            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15391540            Self::ensure_contract_owned(sender, &contract_address)?;15411542            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1543            Ok(())1544        }15451546        /// Set the rate limit for contract sponsoring to specified number of blocks.1547        /// 1548        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1549        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1550        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1551        /// from contract endowment if there are at least B blocks between such transactions. 1552        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1553        /// 1554        /// # Permissions1555        /// 1556        /// * Contract Owner1557        /// 1558        /// # Arguments1559        /// 1560        /// -`contract_address`: Address of the contract to sponsor1561        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1562        /// 1563        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1564        #[transactional]1565        pub fn set_contract_sponsoring_rate_limit(1566            origin,1567            contract_address: T::AccountId,1568            rate_limit: T::BlockNumber1569        ) -> DispatchResult {1570            let sender = ensure_signed(origin)?;15711572            #[cfg(feature = "runtime-benchmarks")]1573            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15741575            Self::ensure_contract_owned(sender, &contract_address)?;1576            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1577            Ok(())1578        }15791580        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1581        /// 1582        /// # Permissions1583        /// 1584        /// * Address that deployed smart contract.1585        /// 1586        /// # Arguments1587        /// 1588        /// -`contract_address`: Address of the contract.1589        /// 1590        /// - `enable`: .  1591        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1592        #[transactional]1593        pub fn toggle_contract_white_list(1594            origin,1595            contract_address: T::AccountId,1596            enable: bool1597        ) -> DispatchResult {1598            let sender = ensure_signed(origin)?;15991600            #[cfg(feature = "runtime-benchmarks")]1601            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16021603            Self::ensure_contract_owned(sender, &contract_address)?;1604            if enable {1605                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1606            } else {1607                <ContractWhiteListEnabled<T>>::remove(contract_address);1608            }1609            Ok(())1610        }1611        1612        /// Add an address to smart contract white list.1613        /// 1614        /// # Permissions1615        /// 1616        /// * Address that deployed smart contract.1617        /// 1618        /// # Arguments1619        /// 1620        /// -`contract_address`: Address of the contract.1621        ///1622        /// -`account_address`: Address to add.1623        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1624        #[transactional]1625        pub fn add_to_contract_white_list(1626            origin,1627            contract_address: T::AccountId,1628            account_address: T::AccountId1629        ) -> DispatchResult {1630            let sender = ensure_signed(origin)?;16311632            #[cfg(feature = "runtime-benchmarks")]1633            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1634            1635            Self::ensure_contract_owned(sender, &contract_address)?;      1636            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1637            Ok(())1638        }16391640        /// Remove an address from smart contract white list.1641        /// 1642        /// # Permissions1643        /// 1644        /// * Address that deployed smart contract.1645        /// 1646        /// # Arguments1647        /// 1648        /// -`contract_address`: Address of the contract.1649        ///1650        /// -`account_address`: Address to remove.1651        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1652        #[transactional]1653        pub fn remove_from_contract_white_list(1654            origin,1655            contract_address: T::AccountId,1656            account_address: T::AccountId1657        ) -> DispatchResult {1658            let sender = ensure_signed(origin)?;16591660            #[cfg(feature = "runtime-benchmarks")]1661            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16621663            Self::ensure_contract_owned(sender, &contract_address)?;1664            <ContractWhiteList<T>>::remove(contract_address, account_address);1665            Ok(())1666        }16671668        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1669        #[transactional]1670        pub fn set_collection_limits(1671            origin,1672            collection_id: u32,1673            new_limits: CollectionLimits<T::BlockNumber>,1674        ) -> DispatchResult {1675            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1676            let mut target_collection = Self::get_collection(collection_id)?;1677            Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1678            let old_limits = &target_collection.limits;1679            let chain_limits = ChainLimit::get();16801681            // collection bounds1682            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1683                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1684                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1685                Error::<T>::CollectionLimitBoundsExceeded);16861687            // token_limit   check  prev1688            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1689            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16901691            ensure!(1692                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1693                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1694                Error::<T>::OwnerPermissionsCantBeReverted,1695            );16961697            target_collection.limits = new_limits;1698            Self::save_collection(target_collection);16991700            Ok(())1701        } 1702    }1703}17041705impl<T: Config> Module<T> {1706    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1707        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1708        Self::validate_create_item_args(&collection, &data)?;1709        Self::create_item_no_validation(&collection, owner, data)?;17101711        Ok(())1712    }17131714    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1715        target_collection.consume_gas(2000000)?;1716        // Limits check1717        Self::is_correct_transfer(target_collection, &recipient)?;17181719        // Transfer permissions check1720        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1721            Self::is_owner_or_admin_permissions(target_collection, &sender),1722            Error::<T>::NoPermission);17231724        if target_collection.access == AccessMode::WhiteList {1725            Self::check_white_list(target_collection, &sender)?;1726            Self::check_white_list(target_collection, &recipient)?;1727        }17281729        match target_collection.mode1730        {1731            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1732            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1733            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1734            _ => ()1735        };17361737        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17381739        Ok(())1740    }17411742	pub fn approve_internal(1743		sender: &T::CrossAccountId,1744		spender: &T::CrossAccountId,1745		collection: &CollectionHandle<T>,1746		item_id: TokenId,1747		amount: u1281748	) -> DispatchResult {1749        collection.consume_gas(2000000)?;1750		Self::token_exists(&collection, item_id)?;17511752		// Transfer permissions check1753		let bypasses_limits = collection.limits.owner_can_transfer &&1754			Self::is_owner_or_admin_permissions(1755				&collection,1756				&sender,1757			);17581759		let allowance_limit = if bypasses_limits {1760			None1761		} else if let Some(amount) = Self::owned_amount(1762			&sender,1763			&collection,1764			item_id,1765		) {1766			Some(amount)1767		} else {1768			fail!(Error::<T>::NoPermission);1769		};17701771		if collection.access == AccessMode::WhiteList {1772			Self::check_white_list(&collection, &sender)?;1773			Self::check_white_list(&collection, &spender)?;1774		}17751776		let allowance: u128 = amount1777			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1778			.ok_or(Error::<T>::NumOverflow)?;1779		if let Some(limit) = allowance_limit {1780			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1781		}1782		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17831784		if matches!(collection.mode, CollectionMode::NFT) {1785			// TODO: NFT: only one owner may exist for token in ERC7211786			collection.log(ERC721Events::Approval {1787                owner: *sender.as_eth(),1788                approved: *spender.as_eth(),1789                token_id: item_id.into(),1790            });1791		}17921793		if matches!(collection.mode, CollectionMode::Fungible(_)) {1794			// TODO: NFT: only one owner may exist for token in ERC201795			collection.log(ERC20Events::Approval {1796                owner: *sender.as_eth(),1797                spender: *spender.as_eth(),1798                value: allowance.into()1799            });1800		}18011802		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1803		Ok(())1804	}18051806	pub fn transfer_from_internal(1807		sender: &T::CrossAccountId,1808		from: &T::CrossAccountId,1809		recipient: &T::CrossAccountId,1810		collection: &CollectionHandle<T>,1811		item_id: TokenId,1812		amount: u128,1813	) -> DispatchResult {1814        collection.consume_gas(2000000)?;1815		// Check approval1816		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));18171818		// Limits check1819		Self::is_correct_transfer(&collection, &recipient)?;18201821		// Transfer permissions check1822		ensure!(1823			approval >= amount || 1824			(1825				collection.limits.owner_can_transfer &&1826				Self::is_owner_or_admin_permissions(&collection, &sender)1827			),1828			Error::<T>::NoPermission1829		);18301831		if collection.access == AccessMode::WhiteList {1832			Self::check_white_list(&collection, &sender)?;1833			Self::check_white_list(&collection, &recipient)?;1834		}18351836		// Reduce approval by transferred amount or remove if remaining approval drops to 01837		let allowance = approval.saturating_sub(amount);1838		if allowance > 0 {1839			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1840		} else {1841			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1842		}18431844		match collection.mode {1845			CollectionMode::NFT => {1846				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1847			}1848			CollectionMode::Fungible(_) => {1849				Self::transfer_fungible(&collection, amount, &from, &recipient)?1850			}1851			CollectionMode::ReFungible => {1852				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1853			}1854			_ => ()1855		};18561857		if matches!(collection.mode, CollectionMode::Fungible(_)) {1858			collection.log(ERC20Events::Approval {1859                owner: *from.as_eth(),1860                spender: *sender.as_eth(),1861                value: allowance.into()1862            });1863		}18641865		Ok(())1866	}18671868    pub fn set_variable_meta_data_internal(1869        sender: &T::CrossAccountId,1870        collection: &CollectionHandle<T>, 1871        item_id: TokenId,1872        data: Vec<u8>,1873    ) -> DispatchResult {1874        Self::token_exists(&collection, item_id)?;18751876        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18771878        // Modify permissions check1879        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1880            Self::is_owner_or_admin_permissions(&collection, &sender),1881            Error::<T>::NoPermission);18821883        match collection.mode1884        {1885            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1886            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1887            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1888            _ => fail!(Error::<T>::UnexpectedCollectionType)1889        };18901891        Ok(())1892    }18931894    pub fn create_multiple_items_internal(1895        sender: &T::CrossAccountId,1896        collection: &CollectionHandle<T>,1897        owner: &T::CrossAccountId,1898        items_data: Vec<CreateItemData>,1899    ) -> DispatchResult {1900        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;19011902        for data in &items_data {1903            Self::validate_create_item_args(&collection, data)?;1904        }1905        for data in &items_data {1906            Self::create_item_no_validation(&collection, owner, data.clone())?;1907        }19081909        Ok(())1910    }19111912    pub fn burn_item_internal(1913        sender: &T::CrossAccountId,1914        collection: &CollectionHandle<T>,1915        item_id: TokenId,1916        value: u128,1917    ) -> DispatchResult {1918        ensure!(1919            Self::is_item_owner(&sender, &collection, item_id) ||1920            (1921                collection.limits.owner_can_transfer &&1922                Self::is_owner_or_admin_permissions(&collection, &sender)1923            ),1924            Error::<T>::NoPermission1925        );19261927        if collection.access == AccessMode::WhiteList {1928            Self::check_white_list(&collection, &sender)?;1929        }19301931        match collection.mode1932        {1933            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1934            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1935            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1936            _ => ()1937        };19381939        Ok(())1940    }19411942    pub fn toggle_white_list_internal(1943        sender: &T::CrossAccountId,1944        collection: &CollectionHandle<T>,1945        address: &T::CrossAccountId,1946        whitelisted: bool,1947    ) -> DispatchResult {1948        Self::check_owner_or_admin_permissions(&collection, &sender)?;19491950        if whitelisted {1951            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1952        } else {1953            <WhiteList<T>>::remove(collection.id, address.as_sub());1954        }19551956        Ok(())1957    }19581959    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1960        let collection_id = collection.id;19611962        // check token limit and account token limit1963        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1964        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1965        1966        Ok(())1967    }19681969    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1970        let collection_id = collection.id;19711972        // check token limit and account token limit1973        let total_items: u32 = ItemListIndex::get(collection_id)1974            .checked_add(amount)1975            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1976        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1977            .checked_add(amount)1978            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1979        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1980        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19811982        if !Self::is_owner_or_admin_permissions(collection, &sender) {1983            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1984            Self::check_white_list(collection, owner)?;1985            Self::check_white_list(collection, sender)?;1986        }19871988        Ok(())1989    }19901991    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1992        match target_collection.mode1993        {1994            CollectionMode::NFT => {1995                if let CreateItemData::NFT(data) = data {1996                    // check sizes1997                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1998                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1999                } else {2000                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);2001                }2002            },2003            CollectionMode::Fungible(_) => {2004                if let CreateItemData::Fungible(_) = data {2005                } else {2006                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);2007                }2008            },2009            CollectionMode::ReFungible => {2010                if let CreateItemData::ReFungible(data) = data {20112012                    // check sizes2013                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);2014                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);20152016                    // Check refungibility limits2017                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2018                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2019                } else {2020                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2021                }2022            },2023            _ => { fail!(Error::<T>::UnexpectedCollectionType); }2024        };20252026        Ok(())2027    }20282029    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2030        match data2031        {2032            CreateItemData::NFT(data) => {2033                let item = NftItemType {2034                    owner: owner.clone(),2035                    const_data: data.const_data,2036                    variable_data: data.variable_data2037                };20382039                Self::add_nft_item(collection, item)?;2040            },2041            CreateItemData::Fungible(data) => {2042                Self::add_fungible_item(collection, &owner, data.value)?;2043            },2044            CreateItemData::ReFungible(data) => {2045                let mut owner_list = Vec::new();2046                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20472048                let item = ReFungibleItemType {2049                    owner: owner_list,2050                    const_data: data.const_data,2051                    variable_data: data.variable_data2052                };20532054                Self::add_refungible_item(collection, item)?;2055            }2056        };20572058        Ok(())2059    }20602061    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2062        let collection_id = collection.id;20632064        // Does new owner already have an account?2065        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20662067        // Mint 2068        let item = FungibleItemType {2069            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2070        };2071        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20722073        // Update balance2074        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2075            .checked_add(value)2076            .ok_or(Error::<T>::NumOverflow)?;2077        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20782079        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2080        Ok(())2081    }20822083    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2084        let collection_id = collection.id;20852086        let current_index = <ItemListIndex>::get(collection_id)2087            .checked_add(1)2088            .ok_or(Error::<T>::NumOverflow)?;2089        let itemcopy = item.clone();20902091        ensure!(2092            item.owner.len() == 1,2093            Error::<T>::BadCreateRefungibleCall,2094        );2095        let item_owner = item.owner.first().expect("only one owner is defined");20962097        let value = item_owner.fraction;2098        let owner = item_owner.owner.clone();20992100        Self::add_token_index(collection_id, current_index, &owner)?;21012102        <ItemListIndex>::insert(collection_id, current_index);2103        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);21042105        // Update balance2106        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2107            .checked_add(value)2108            .ok_or(Error::<T>::NumOverflow)?;2109        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21102111        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2112        Ok(())2113    }21142115    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2116        let collection_id = collection.id;21172118        let current_index = <ItemListIndex>::get(collection_id)2119            .checked_add(1)2120            .ok_or(Error::<T>::NumOverflow)?;21212122        let item_owner = item.owner.clone();2123        Self::add_token_index(collection_id, current_index, &item.owner)?;21242125        <ItemListIndex>::insert(collection_id, current_index);2126        <NftItemList<T>>::insert(collection_id, current_index, item);21272128        // Update balance2129        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2130            .checked_add(1)2131            .ok_or(Error::<T>::NumOverflow)?;2132        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21332134        collection.log(ERC721Events::Transfer {2135            from: H160::default(),2136            to: *item_owner.as_eth(),2137            token_id: current_index.into(),2138        });2139        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2140        Ok(())2141    }21422143    fn burn_refungible_item(2144        collection: &CollectionHandle<T>,2145        item_id: TokenId,2146        owner: &T::CrossAccountId,2147    ) -> DispatchResult {2148        let collection_id = collection.id;21492150        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2151            .ok_or(Error::<T>::TokenNotFound)?;2152        let rft_balance = token2153            .owner2154            .iter()2155            .find(|&i| i.owner == *owner)2156            .ok_or(Error::<T>::TokenNotFound)?;2157        Self::remove_token_index(collection_id, item_id, owner)?;21582159        // update balance2160        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2161            .checked_sub(rft_balance.fraction)2162            .ok_or(Error::<T>::NumOverflow)?;2163        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21642165        // Re-create owners list with sender removed2166        let index = token2167            .owner2168            .iter()2169            .position(|i| i.owner == *owner)2170            .expect("owned item is exists");2171        token.owner.remove(index);2172        let owner_count = token.owner.len();21732174        // Burn the token completely if this was the last (only) owner2175        if owner_count == 0 {2176            <ReFungibleItemList<T>>::remove(collection_id, item_id);2177            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2178        }2179        else {2180            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2181        }21822183        Ok(())2184    }21852186    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2187        let collection_id = collection.id;21882189        let item = <NftItemList<T>>::get(collection_id, item_id)2190            .ok_or(Error::<T>::TokenNotFound)?;2191        Self::remove_token_index(collection_id, item_id, &item.owner)?;21922193        // update balance2194        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2195            .checked_sub(1)2196            .ok_or(Error::<T>::NumOverflow)?;2197        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2198        <NftItemList<T>>::remove(collection_id, item_id);2199        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);22002201        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2202        Ok(())2203    }22042205    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2206        let collection_id = collection.id;22072208        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2209        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);22102211        // update balance2212        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2213            .checked_sub(value)2214            .ok_or(Error::<T>::NumOverflow)?;2215        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22162217        if balance.value - value > 0 {2218            balance.value -= value;2219            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2220        }2221        else {2222            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2223        }22242225        collection.log(ERC20Events::Transfer {2226            from: *owner.as_eth(),2227            to: H160::default(),2228            value: value.into(),2229        });2230        Ok(())2231    }22322233    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2234        Ok(<CollectionHandle<T>>::get(collection_id)2235            .ok_or(Error::<T>::CollectionNotFound)?)2236    }22372238    fn save_collection(collection: CollectionHandle<T>) {2239        <CollectionById<T>>::insert(collection.id, collection.into_inner());2240    }22412242    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2243        if collection.logs.is_empty() {2244            return Ok(())2245        }2246        T::EthereumTransactionSender::submit_logs_transaction(2247            eth::generate_transaction(collection.id, T::EthereumChainId::get()),2248            collection.logs.retrieve_logs(),2249        )2250    }22512252    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2253        ensure!(2254            *subject == target_collection.owner,2255            Error::<T>::NoPermission2256        );22572258        Ok(())2259    }22602261    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2262        *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2263    }22642265    fn check_owner_or_admin_permissions(2266        collection: &CollectionHandle<T>,2267        subject: &T::CrossAccountId,2268    ) -> DispatchResult {2269        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22702271        Ok(())2272    }22732274    fn owned_amount(2275        subject: &T::CrossAccountId,2276        target_collection: &CollectionHandle<T>,2277        item_id: TokenId,2278    ) -> Option<u128> {2279        let collection_id = target_collection.id;22802281        match target_collection.mode {2282            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2283                .then(|| 1),2284            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2285                .value),2286            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2287                .owner2288                .iter()2289                .find(|i| i.owner == *subject)2290                .map(|i| i.fraction),2291            CollectionMode::Invalid => None,2292        }2293    }22942295    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2296        match target_collection.mode {2297            CollectionMode::Fungible(_) => true,2298            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2299        }2300    }23012302    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2303        let collection_id = collection.id;23042305        let mes = Error::<T>::AddresNotInWhiteList;2306        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);23072308        Ok(())2309    }23102311    /// Check if token exists. In case of Fungible, check if there is an entry for 2312    /// the owner in fungible balances double map2313    fn token_exists(2314        target_collection: &CollectionHandle<T>,2315        item_id: TokenId,2316    ) -> DispatchResult {2317        let collection_id = target_collection.id;2318        let exists = match target_collection.mode2319        {2320            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2321            CollectionMode::Fungible(_)  => true,2322            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2323            _ => false2324        };23252326        ensure!(exists == true, Error::<T>::TokenNotFound);2327        Ok(())2328    }23292330    fn transfer_fungible(2331        collection: &CollectionHandle<T>,2332        value: u128,2333        owner: &T::CrossAccountId,2334        recipient: &T::CrossAccountId,2335    ) -> DispatchResult {2336        let collection_id = collection.id;23372338        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2339        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23402341        // Send balance to recipient (updates balanceOf of recipient)2342        Self::add_fungible_item(collection, recipient, value)?;23432344        // update balanceOf of sender2345        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23462347        // Reduce or remove sender2348        if balance.value == value {2349            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2350        }2351        else {2352            balance.value -= value;2353            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2354        }23552356        collection.log(ERC20Events::Transfer {2357            from: *owner.as_eth(),2358            to: *recipient.as_eth(),2359            value: value.into(),2360        });2361        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23622363        Ok(())2364    }23652366    fn transfer_refungible(2367        collection: &CollectionHandle<T>,2368        item_id: TokenId,2369        value: u128,2370        owner: T::CrossAccountId,2371        new_owner: T::CrossAccountId,2372    ) -> DispatchResult {2373        let collection_id = collection.id;2374        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2375            .ok_or(Error::<T>::TokenNotFound)?;23762377        let item = full_item2378            .owner2379            .iter()2380            .filter(|i| i.owner == owner)2381            .next()2382            .ok_or(Error::<T>::TokenNotFound)?;2383        let amount = item.fraction;23842385        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23862387        // update balance2388        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2389            .checked_sub(value)2390            .ok_or(Error::<T>::NumOverflow)?;2391        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23922393        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2394            .checked_add(value)2395            .ok_or(Error::<T>::NumOverflow)?;2396        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23972398        let old_owner = item.owner.clone();2399        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);24002401        // transfer2402        if amount == value && !new_owner_has_account {2403            // change owner2404            // new owner do not have account2405            let mut new_full_item = full_item.clone();2406            new_full_item2407                .owner2408                .iter_mut()2409                .find(|i| i.owner == owner)2410                .expect("old owner does present in refungible")2411                .owner = new_owner.clone();2412            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24132414            // update index collection2415            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2416        } else {2417            let mut new_full_item = full_item.clone();2418            new_full_item2419                .owner2420                .iter_mut()2421                .find(|i| i.owner == owner)2422                .expect("old owner does present in refungible")2423                .fraction -= value;24242425            // separate amount2426            if new_owner_has_account {2427                // new owner has account2428                new_full_item2429                    .owner2430                    .iter_mut()2431                    .find(|i| i.owner == new_owner)2432                    .expect("new owner has account")2433                    .fraction += value;2434            } else {2435                // new owner do not have account2436                new_full_item.owner.push(Ownership {2437                    owner: new_owner.clone(),2438                    fraction: value,2439                });2440                Self::add_token_index(collection_id, item_id, &new_owner)?;2441            }24422443            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2444        }24452446        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24472448        Ok(())2449    }24502451    fn transfer_nft(2452        collection: &CollectionHandle<T>,2453        item_id: TokenId,2454        sender: T::CrossAccountId,2455        new_owner: T::CrossAccountId,2456    ) -> DispatchResult {2457        let collection_id = collection.id;2458        let mut item = <NftItemList<T>>::get(collection_id, item_id)2459            .ok_or(Error::<T>::TokenNotFound)?;24602461        ensure!(2462            sender == item.owner,2463            Error::<T>::MustBeTokenOwner2464        );24652466        // update balance2467        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2468            .checked_sub(1)2469            .ok_or(Error::<T>::NumOverflow)?;2470        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24712472        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2473            .checked_add(1)2474            .ok_or(Error::<T>::NumOverflow)?;2475        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24762477        // change owner2478        let old_owner = item.owner.clone();2479        item.owner = new_owner.clone();2480        <NftItemList<T>>::insert(collection_id, item_id, item);24812482        // update index collection2483        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24842485        collection.log(ERC721Events::Transfer {2486            from: *sender.as_eth(),2487            to: *new_owner.as_eth(),2488            token_id: item_id.into(),2489        });2490        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24912492        Ok(())2493    }2494    2495    fn set_re_fungible_variable_data(2496        collection: &CollectionHandle<T>,2497        item_id: TokenId,2498        data: Vec<u8>2499    ) -> DispatchResult {2500        let collection_id = collection.id;2501        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2502            .ok_or(Error::<T>::TokenNotFound)?;25032504        item.variable_data = data;25052506        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25072508        Ok(())2509    }25102511    fn set_nft_variable_data(2512        collection: &CollectionHandle<T>,2513        item_id: TokenId,2514        data: Vec<u8>2515    ) -> DispatchResult {2516        let collection_id = collection.id;2517        let mut item = <NftItemList<T>>::get(collection_id, item_id)2518            .ok_or(Error::<T>::TokenNotFound)?;2519        2520        item.variable_data = data;25212522        <NftItemList<T>>::insert(collection_id, item_id, item);2523        2524        Ok(())2525    }25262527    fn init_collection(item: &Collection<T>) {2528        // check params2529        assert!(2530            item.decimal_points <= MAX_DECIMAL_POINTS,2531            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2532        );2533        assert!(2534            item.name.len() <= 64,2535            "Collection name can not be longer than 63 char"2536        );2537        assert!(2538            item.name.len() <= 256,2539            "Collection description can not be longer than 255 char"2540        );2541        assert!(2542            item.token_prefix.len() <= 16,2543            "Token prefix can not be longer than 15 char"2544        );25452546        // Generate next collection ID2547        let next_id = CreatedCollectionCount::get()2548            .checked_add(1)2549            .unwrap();25502551        CreatedCollectionCount::put(next_id);2552    }25532554    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2555        let current_index = <ItemListIndex>::get(collection_id)2556            .checked_add(1)2557            .unwrap();25582559        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25602561        <ItemListIndex>::insert(collection_id, current_index);25622563        // Update balance2564        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2565            .checked_add(1)2566            .unwrap();2567        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2568    }25692570    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2571        let current_index = <ItemListIndex>::get(collection_id)2572            .checked_add(1)2573            .unwrap();25742575        Self::add_token_index(collection_id, current_index, owner).unwrap();25762577        <ItemListIndex>::insert(collection_id, current_index);25782579        // Update balance2580        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2581            .checked_add(item.value)2582            .unwrap();2583        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2584    }25852586    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2587        let current_index = <ItemListIndex>::get(collection_id)2588            .checked_add(1)2589            .unwrap();25902591        let value = item.owner.first().unwrap().fraction;2592        let owner = item.owner.first().unwrap().owner.clone();25932594        Self::add_token_index(collection_id, current_index, &owner).unwrap();25952596        <ItemListIndex>::insert(collection_id, current_index);25972598        // Update balance2599        let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2600            .checked_add(value)2601            .unwrap();2602        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2603    }26042605    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2606        // add to account limit2607        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26082609            // bound Owned tokens by a single address2610            let count = <AccountItemCount<T>>::get(owner.as_sub());2611            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26122613            <AccountItemCount<T>>::insert(owner.as_sub(), count2614                .checked_add(1)2615                .ok_or(Error::<T>::NumOverflow)?);2616        }2617        else {2618            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2619        }26202621        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2622        if list_exists {2623            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2624            let item_contains = list.contains(&item_index.clone());26252626            if !item_contains {2627                list.push(item_index.clone());2628            }26292630            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2631        } else {2632            let mut itm = Vec::new();2633            itm.push(item_index.clone());2634            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2635        }26362637        Ok(())2638    }26392640    fn remove_token_index(2641        collection_id: CollectionId,2642        item_index: TokenId,2643        owner: &T::CrossAccountId,2644    ) -> DispatchResult {26452646        // update counter2647        <AccountItemCount<T>>::insert(owner.as_sub(), 2648            <AccountItemCount<T>>::get(owner.as_sub())2649            .checked_sub(1)2650            .ok_or(Error::<T>::NumOverflow)?);265126522653        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2654        if list_exists {2655            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2656            let item_contains = list.contains(&item_index.clone());26572658            if item_contains {2659                list.retain(|&item| item != item_index);2660                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2661            }2662        }26632664        Ok(())2665    }26662667    fn move_token_index(2668        collection_id: CollectionId,2669        item_index: TokenId,2670        old_owner: &T::CrossAccountId,2671        new_owner: &T::CrossAccountId,2672    ) -> DispatchResult {2673        Self::remove_token_index(collection_id, item_index, old_owner)?;2674        Self::add_token_index(collection_id, item_index, new_owner)?;26752676        Ok(())2677    }2678    2679    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2680        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26812682        Ok(())2683    }2684}26852686////////////////////////////////////////////////////////////////////////////////////////////////////2687// Economic models2688// #region26892690/// Fee multiplier.2691pub type Multiplier = FixedU128;26922693type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26942695/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2696/// in the queue.2697#[derive(Encode, Decode, Clone, Eq, PartialEq)]2698pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26992700impl<T: Config + Send + Sync> sp_std::fmt::Debug 2701    for ChargeTransactionPayment<T>2702{2703	#[cfg(feature = "std")]2704	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2705		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2706	}2707	#[cfg(not(feature = "std"))]2708	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2709		Ok(())2710	}2711}27122713impl<T: Config> ChargeTransactionPayment<T>2714where2715    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2716    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2717    T::AccountId: AsRef<[u8]>,2718    T::AccountId: UncheckedFrom<T::Hash>,2719{2720    fn traditional_fee(2721        len: usize,2722        info: &DispatchInfoOf<T::Call>,2723        tip: BalanceOf<T>,2724    ) -> BalanceOf<T>2725    where2726        T::Call: Dispatchable<Info = DispatchInfo>,2727    {2728        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2729    }27302731	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2732        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2733        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2734        let len_saturation = max_block_length as u64 / (len as u64).max(1);2735        let coefficient: BalanceOf<T> = weight_saturation2736            .min(len_saturation)2737            .saturated_into::<BalanceOf<T>>();2738        final_fee2739            .saturating_mul(coefficient)2740            .saturated_into::<TransactionPriority>()2741    }27422743    fn withdraw_fee(2744        &self,2745        who: &T::AccountId,2746        call: &T::Call,2747        info: &DispatchInfoOf<T::Call>,2748        len: usize,2749	) -> Result<2750		(2751			BalanceOf<T>,2752			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2753		),2754		TransactionValidityError,2755	> {2756        let tip = self.0;27572758        let fee = Self::traditional_fee(len, info, tip);27592760        // Only mess with balances if fee is not zero.2761        if fee.is_zero() {2762            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2763			.map(|i| (fee, i));2764        }27652766        // Determine who is paying transaction fee based on ecnomic model2767        // Parse call to extract collection ID and access collection sponsor2768        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2769            Some(Call::create_item(collection_id, _owner, _properties)) => {2770                let collection = <CollectionById<T>>::get(collection_id)?;27712772                // sponsor timeout2773                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27742775                let limit = collection.limits.sponsor_transfer_timeout;2776                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2777                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2778                    let limit_time = last_tx_block + limit.into();2779                    if block_number <= limit_time {2780                        return None;2781                    }2782                }2783                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27842785                // check free create limit2786                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2787                    collection.sponsorship.sponsor()2788                        .cloned()2789                } else {2790                    None2791                }2792            }2793            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2794                let collection = <CollectionById<T>>::get(collection_id)?;2795                2796                let mut sponsor_transfer = false;2797                if collection.sponsorship.confirmed() {27982799                    let collection_limits = collection.limits;2800                    let collection_mode = collection.mode;2801    2802                    // sponsor timeout2803                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2804                    sponsor_transfer = match collection_mode {2805                        CollectionMode::NFT => {2806    2807                            // get correct limit2808                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2809                                collection_limits.sponsor_transfer_timeout2810                            } else {2811                                ChainLimit::get().nft_sponsor_transfer_timeout2812                            };2813    2814                            let mut sponsored = true;2815                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2816                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2817                                let limit_time = last_tx_block + limit.into();2818                                if block_number <= limit_time {2819                                    sponsored = false;2820                                }2821                            }2822                            if sponsored {2823                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2824                            }28252826                            sponsored2827                        }2828                        CollectionMode::Fungible(_) => {2829    2830                            // get correct limit2831                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2832                                collection_limits.sponsor_transfer_timeout2833                            } else {2834                                ChainLimit::get().fungible_sponsor_transfer_timeout2835                            };2836    2837                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2838                            let mut sponsored = true;2839                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2840                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2841                                let limit_time = last_tx_block + limit.into();2842                                if block_number <= limit_time {2843                                    sponsored = false;2844                                }2845                            }2846                            if sponsored {2847                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2848                            }28492850                            sponsored2851                        }2852                        CollectionMode::ReFungible => {2853    2854                            // get correct limit2855                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2856                                collection_limits.sponsor_transfer_timeout2857                            } else {2858                                ChainLimit::get().refungible_sponsor_transfer_timeout2859                            };2860    2861                            let mut sponsored = true;2862                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2863                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2864                                let limit_time = last_tx_block + limit.into();2865                                if block_number <= limit_time {2866                                    sponsored = false;2867                                }2868                            }2869                            if sponsored {2870                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2871                            }28722873                            sponsored2874                        }2875                        _ => {2876                            false2877                        },2878                    };2879                }28802881                if !sponsor_transfer {2882                    None2883                } else {2884                    collection.sponsorship.sponsor()2885                        .cloned()2886                }2887            }28882889            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2890                let mut sponsor_metadata_changes = false;28912892                let collection = <CollectionById<T>>::get(collection_id)?;28932894                if2895                    collection.sponsorship.confirmed() &&2896                    // Can't sponsor fungible collection, this tx will be rejected2897                    // as invalid2898                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2899                    data.len() <= collection.limits.sponsored_data_size as usize2900                {2901                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2902                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;29032904                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2905                            .map(|last_block| block_number - last_block > rate_limit)2906                            .unwrap_or(true) 2907                        {2908                            sponsor_metadata_changes = true;2909                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2910                        }2911                    }2912                }29132914                if !sponsor_metadata_changes {2915                    None2916                } else {2917                    collection.sponsorship.sponsor().cloned()2918                }2919            }29202921            _ => None,2922        })();29232924        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2925            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29262927                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29282929                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2930                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2931                  2932                if !owned_contract && white_list_enabled {2933                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2934                        return Err(InvalidTransaction::Call.into());2935                    }2936                }2937            },2938            _ => {},2939        }29402941        // Sponsor smart contracts2942        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29432944            // On instantiation: set the contract owner2945            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29462947                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2948                    &who,2949                    code_hash,2950                    salt,2951                );2952                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29532954                None2955            },29562957            // On instantiation with code: set the contract owner2958            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {29592960                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2961                    &who,2962                    &T::Hashing::hash(&_code),2963                    _salt,2964                );29652966                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29672968                None2969            }29702971            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2972            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29732974                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29752976                let mut sponsor_transfer = false;2977                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2978                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2979                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2980                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2981                    let limit_time = last_tx_block + rate_limit;29822983                    if block_number >= limit_time {2984                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2985                        sponsor_transfer = true;2986                    }2987                } else {2988                    sponsor_transfer = false;2989                }2990               2991                if sponsor_transfer {2992                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2993                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2994                            return Some(called_contract);2995                        }2996                    }2997                }29982999                None3000            },30013002            _ => None,3003        });30043005        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30063007		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3008			.map(|i| (fee, i))3009    }3010}301130123013impl<T: Config + Send + Sync> SignedExtension3014    for ChargeTransactionPayment<T>3015where3016    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3017    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3018    T::AccountId: AsRef<[u8]>,3019    T::AccountId: UncheckedFrom<T::Hash>,3020{3021    const IDENTIFIER: &'static str = "ChargeTransactionPayment";3022    type AccountId = T::AccountId;3023    type Call = T::Call;3024    type AdditionalSigned = ();3025    type Pre = (3026        // tip3027        BalanceOf<T>,3028        // who pays fee3029        Self::AccountId,3030		// imbalance resulting from withdrawing the fee3031		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3032    );3033    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3034        Ok(())3035    }30363037    fn validate(3038        &self,3039        who: &Self::AccountId,3040        call: &Self::Call,3041        info: &DispatchInfoOf<Self::Call>,3042        len: usize,3043    ) -> TransactionValidity {3044		let (fee, _) = self.withdraw_fee(who, call, info, len)?;3045		Ok(ValidTransaction {3046			priority: Self::get_priority(len, info, fee),3047			..Default::default()3048		})3049    }30503051    fn pre_dispatch(3052        self,3053        who: &Self::AccountId,3054        call: &Self::Call,3055        info: &DispatchInfoOf<Self::Call>,3056        len: usize,3057    ) -> Result<Self::Pre, TransactionValidityError> {3058        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3059        Ok((self.0, who.clone(), imbalance))3060    }30613062    fn post_dispatch(3063        pre: Self::Pre,3064        info: &DispatchInfoOf<Self::Call>,3065        post_info: &PostDispatchInfoOf<Self::Call>,3066        len: usize,3067        _result: &DispatchResult,3068    ) -> Result<(), TransactionValidityError> {3069		let (tip, who, imbalance) = pre;3070		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3071			len as u32,3072			info,3073			post_info,3074			tip,3075		);3076		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3077		Ok(())3078    }3079}30803081// #endregion30823083sp_api::decl_runtime_apis! {3084    pub trait NftApi {3085        /// Used for ethereum integration3086        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3087    }3088}