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

difftreelog

source

pallets/nft/src/lib.rs112.5 KiBsourcehistory
1//2// This file is subject to the terms and conditions defined in3// file 'LICENSE', which is part of this source code package.4//56#![recursion_limit = "1024"]78#![cfg_attr(not(feature = "std"), no_std)]910#[cfg(feature = "std")]11pub use serde::*;1213use core::ops::{Deref, DerefMut};14use codec::{Decode, Encode};15pub use frame_support::{16    construct_runtime, decl_event, decl_module, decl_storage, decl_error,17    dispatch::DispatchResult,18    ensure, fail, parameter_types,19    traits::{20        Currency, ExistenceRequirement, Get, Imbalance, KeyOwnerProofSystem, OnUnbalanced,21        Randomness, IsSubType, WithdrawReasons,22    },23    weights::{24        constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND},25        DispatchInfo, GetDispatchInfo, IdentityFee, Pays, PostDispatchInfo, Weight,26        WeightToFeePolynomial, DispatchClass,27    },28    StorageValue,29    transactional,30};3132use frame_system::{self as system, ensure_signed, ensure_root};33use sp_core::{H160, H256};34use sp_runtime::sp_std::prelude::Vec;35use sp_runtime::{36    traits::{37        Hash, DispatchInfoOf, Dispatchable, PostDispatchInfoOf, Saturating, SaturatedConversion, SignedExtension, Zero,38    },39    transaction_validity::{40        TransactionPriority, InvalidTransaction, TransactionValidity, TransactionValidityError, ValidTransaction,41    },42    FixedPointOperand, FixedU128,43};44use sp_runtime::traits::StaticLookup;45use pallet_contracts::chain_extension::UncheckedFrom;46use pallet_ethereum::EthereumTransactionSender;47use pallet_transaction_payment::OnChargeTransaction;4849#[cfg(test)]50mod mock;5152#[cfg(test)]53mod tests;5455mod default_weights;56mod eth;5758pub use eth::NftErcSupport;59pub use eth::account::*;6061pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;62pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;63pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;64pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6566// Structs67// #region6869pub type CollectionId = u32;70pub type TokenId = u32;71pub type DecimalPoints = u8;7273#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]74#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]75pub enum CollectionMode {76    Invalid,77    NFT,78    // decimal points79    Fungible(DecimalPoints),80    ReFungible,81}8283impl Default for CollectionMode {84    fn default() -> Self {85        Self::Invalid86    }87}8889impl Into<u8> for CollectionMode {90    fn into(self) -> u8 {91        match self {92            CollectionMode::Invalid => 0,93            CollectionMode::NFT => 1,94            CollectionMode::Fungible(_) => 2,95            CollectionMode::ReFungible => 3,96        }97    }98}99100#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]101#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]102pub enum AccessMode {103    Normal,104    WhiteList,105}106impl Default for AccessMode {107    fn default() -> Self {108        Self::Normal109    }110}111112#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]113#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]114pub enum SchemaVersion {115    ImageURL,116    Unique,117}118impl Default for SchemaVersion {119    fn default() -> Self {120        Self::ImageURL121    }122}123124#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]125#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]126pub struct Ownership<AccountId> {127    pub owner: AccountId,128    pub fraction: u128,129}130131#[derive(Encode, Decode, Debug, Clone, PartialEq)]132#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]133pub enum SponsorshipState<AccountId> {134    /// The fees are applied to the transaction sender135    Disabled,136    Unconfirmed(AccountId),137    /// Transactions are sponsored by specified account138    Confirmed(AccountId),139}140141impl<AccountId> SponsorshipState<AccountId> {142    fn sponsor(&self) -> Option<&AccountId> {143        match self {144            Self::Confirmed(sponsor) => Some(sponsor),145            _ => None,146        }147    }148149    fn pending_sponsor(&self) -> Option<&AccountId> {150        match self {151            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),152            _ => None,153        }154    }155156    fn confirmed(&self) -> bool {157        matches!(self, Self::Confirmed(_))158    }159}160161impl<T> Default for SponsorshipState<T> {162    fn default() -> Self {163        Self::Disabled164    }165}166167#[derive(Encode, Decode, Clone, PartialEq)]168#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]169pub struct Collection<T: Config> {170    pub owner: T::CrossAccountId,171    pub mode: CollectionMode,172    pub access: AccessMode,173    pub decimal_points: DecimalPoints,174    pub name: Vec<u16>,        // 64 include null escape char175    pub description: Vec<u16>, // 256 include null escape char176    pub token_prefix: Vec<u8>, // 16 include null escape char177    pub mint_mode: bool,178    pub offchain_schema: Vec<u8>,179    pub schema_version: SchemaVersion,180    pub sponsorship: SponsorshipState<T::AccountId>,181    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 182    pub variable_on_chain_schema: Vec<u8>, //183    pub const_on_chain_schema: Vec<u8>, //184}185186pub struct CollectionHandle<T: Config> {187    pub id: CollectionId,188    collection: Collection<T>,189    logs: eth::log::LogRecorder,190}191impl<T: Config> CollectionHandle<T> {192	pub fn get(id: CollectionId) -> Option<Self> {193		<CollectionById<T>>::get(id)194			.map(|collection| Self {195				id,196				collection,197                logs: eth::log::LogRecorder::default(),198			})199	}200    pub fn log(&self, topics: Vec<H256>, data: eth::abi::AbiWriter) {201        self.logs.log(topics, data)202    }203    pub fn into_inner(self) -> Collection<T> {204        self.collection.clone()205    }206}207208impl<T: Config> Deref for CollectionHandle<T> {209    type Target = Collection<T>;210211    fn deref(&self) -> &Self::Target {212        &self.collection213    }214}215216impl<T: Config> DerefMut for CollectionHandle<T> {217    fn deref_mut(&mut self) -> &mut Self::Target {218        &mut self.collection219    }220}221222#[derive(Encode, Decode, Debug, Clone, PartialEq)]223#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]224pub struct NftItemType<AccountId> {225    pub owner: AccountId,226    pub const_data: Vec<u8>,227    pub variable_data: Vec<u8>,228}229230#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]231#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]232pub struct FungibleItemType {233    pub value: u128,234}235236#[derive(Encode, Decode, Debug, Clone, PartialEq)]237#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]238pub struct ReFungibleItemType<AccountId> {239    pub owner: Vec<Ownership<AccountId>>,240    pub const_data: Vec<u8>,241    pub variable_data: Vec<u8>,242}243244// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]245// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]246// pub struct VestingItem<AccountId, Moment> {247//     pub sender: AccountId,248//     pub recipient: AccountId,249//     pub collection_id: CollectionId,250//     pub item_id: TokenId,251//     pub amount: u64,252//     pub vesting_date: Moment,253// }254255#[derive(Encode, Decode, Debug, Clone, PartialEq)]256#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]257pub struct CollectionLimits<BlockNumber: Encode + Decode> {258    pub account_token_ownership_limit: u32,259    pub sponsored_data_size: u32,260    /// None - setVariableMetadata is not sponsored261    /// Some(v) - setVariableMetadata is sponsored 262    ///           if there is v block between txs263    pub sponsored_data_rate_limit: Option<BlockNumber>,264    pub token_limit: u32,265266    // Timeouts for item types in passed blocks267    pub sponsor_transfer_timeout: u32,268    pub owner_can_transfer: bool,269    pub owner_can_destroy: bool,270}271272impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {273    fn default() -> Self {274        Self { 275            account_token_ownership_limit: 10_000_000, 276            token_limit: u32::max_value(),277            sponsored_data_size: u32::MAX, 278            sponsored_data_rate_limit: None,279            sponsor_transfer_timeout: 14400,280            owner_can_transfer: true,281            owner_can_destroy: true282        }283    }284}285286#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]287#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]288pub struct ChainLimits {289    pub collection_numbers_limit: u32,290    pub account_token_ownership_limit: u32,291    pub collections_admins_limit: u64,292    pub custom_data_limit: u32,293294    // Timeouts for item types in passed blocks295    pub nft_sponsor_transfer_timeout: u32,296    pub fungible_sponsor_transfer_timeout: u32,297    pub refungible_sponsor_transfer_timeout: u32,298299    // Schema limits300    pub offchain_schema_limit: u32,301    pub variable_on_chain_schema_limit: u32,302    pub const_on_chain_schema_limit: u32,303}304305pub trait WeightInfo {306	fn create_collection() -> Weight;307	fn destroy_collection() -> Weight;308	fn add_to_white_list() -> Weight;309	fn remove_from_white_list() -> Weight;310    fn set_public_access_mode() -> Weight;311    fn set_mint_permission() -> Weight;312    fn change_collection_owner() -> Weight;313    fn add_collection_admin() -> Weight;314    fn remove_collection_admin() -> Weight;315    fn set_collection_sponsor() -> Weight;316    fn confirm_sponsorship() -> Weight;317    fn remove_collection_sponsor() -> Weight;318    fn create_item(s: usize) -> Weight;319    fn burn_item() -> Weight;320    fn transfer() -> Weight;321    fn approve() -> Weight;322    fn transfer_from() -> Weight;323    fn set_offchain_schema() -> Weight;324    fn set_const_on_chain_schema() -> Weight;325    fn set_variable_on_chain_schema() -> Weight;326    fn set_variable_meta_data() -> Weight;327    fn enable_contract_sponsoring() -> Weight;328    fn set_schema_version() -> Weight;329    fn set_chain_limits() -> Weight;330    fn set_contract_sponsoring_rate_limit() -> Weight;331    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;332    fn toggle_contract_white_list() -> Weight;333    fn add_to_contract_white_list() -> Weight;334    fn remove_from_contract_white_list() -> Weight;335    fn set_collection_limits() -> Weight;336}337338#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]339#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]340pub struct CreateNftData {341    pub const_data: Vec<u8>,342    pub variable_data: Vec<u8>,343}344345#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]346#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]347pub struct CreateFungibleData {348    pub value: u128,349}350351#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]352#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]353pub struct CreateReFungibleData {354    pub const_data: Vec<u8>,355    pub variable_data: Vec<u8>,356    pub pieces: u128,357}358359#[derive(Encode, Decode, Debug, Clone, PartialEq)]360#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]361pub enum CreateItemData {362    NFT(CreateNftData),363    Fungible(CreateFungibleData),364    ReFungible(CreateReFungibleData),365}366367impl CreateItemData {368    pub fn len(&self) -> usize {369        let len = match self {370            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),371            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),372            _ => 0373        };374        375        return len;376    }377}378379impl From<CreateNftData> for CreateItemData {380    fn from(item: CreateNftData) -> Self {381        CreateItemData::NFT(item)382    }383}384385impl From<CreateReFungibleData> for CreateItemData {386    fn from(item: CreateReFungibleData) -> Self {387        CreateItemData::ReFungible(item)388    }389}390391impl From<CreateFungibleData> for CreateItemData {392    fn from(item: CreateFungibleData) -> Self {393        CreateItemData::Fungible(item)394    }395}396397398decl_error! {399	/// Error for non-fungible-token module.400	pub enum Error for Module<T: Config> {401        /// Total collections bound exceeded.402        TotalCollectionsLimitExceeded,403		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.404        CollectionDecimalPointLimitExceeded, 405        /// Collection name can not be longer than 63 char.406        CollectionNameLimitExceeded, 407        /// Collection description can not be longer than 255 char.408        CollectionDescriptionLimitExceeded, 409        /// Token prefix can not be longer than 15 char.410        CollectionTokenPrefixLimitExceeded,411        /// This collection does not exist.412        CollectionNotFound,413        /// Item not exists.414        TokenNotFound,415        /// Admin not found416        AdminNotFound,417        /// Arithmetic calculation overflow.418        NumOverflow,       419        /// Account already has admin role.420        AlreadyAdmin,  421        /// You do not own this collection.422        NoPermission,423        /// This address is not set as sponsor, use setCollectionSponsor first.424        ConfirmUnsetSponsorFail,425        /// Collection is not in mint mode.426        PublicMintingNotAllowed,427        /// Sender parameter and item owner must be equal.428        MustBeTokenOwner,429        /// Item balance not enough.430        TokenValueTooLow,431        /// Size of item is too large.432        NftSizeLimitExceeded,433        /// No approve found434        ApproveNotFound,435        /// Requested value more than approved.436        TokenValueNotEnough,437        /// Only approved addresses can call this method.438        ApproveRequired,439        /// Address is not in white list.440        AddresNotInWhiteList,441        /// Number of collection admins bound exceeded.442        CollectionAdminsLimitExceeded,443        /// Owned tokens by a single address bound exceeded.444        AddressOwnershipLimitExceeded,445        /// Length of items properties must be greater than 0.446        EmptyArgument,447        /// const_data exceeded data limit.448        TokenConstDataLimitExceeded,449        /// variable_data exceeded data limit.450        TokenVariableDataLimitExceeded,451        /// Not NFT item data used to mint in NFT collection.452        NotNftDataUsedToMintNftCollectionToken,453        /// Not Fungible item data used to mint in Fungible collection.454        NotFungibleDataUsedToMintFungibleCollectionToken,455        /// Not Re Fungible item data used to mint in Re Fungible collection.456        NotReFungibleDataUsedToMintReFungibleCollectionToken,457        /// Unexpected collection type.458        UnexpectedCollectionType,459        /// Can't store metadata in fungible tokens.460        CantStoreMetadataInFungibleTokens,461        /// Collection token limit exceeded462        CollectionTokenLimitExceeded,463        /// Account token limit exceeded per collection464        AccountTokenLimitExceeded,465        /// Collection limit bounds per collection exceeded466        CollectionLimitBoundsExceeded,467        /// Tried to enable permissions which are only permitted to be disabled468        OwnerPermissionsCantBeReverted,469        /// Schema data size limit bound exceeded470        SchemaDataLimitExceeded,471        /// Maximum refungibility exceeded472        WrongRefungiblePieces,473        /// createRefungible should be called with one owner474        BadCreateRefungibleCall,475	}476}477478pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {479    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;480481    /// Weight information for extrinsics in this pallet.482	type WeightInfo: WeightInfo;483484    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;485    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;486    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;487488	type CrossAccountId: CrossAccountId<Self::AccountId>;489    type Currency: Currency<Self::AccountId>;490    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;491    type TreasuryAccountId: Get<Self::AccountId>;492493    type EthereumChainId: Get<u64>;494    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;495}496497#[cfg(feature = "runtime-benchmarks")]498mod benchmarking;499500// #endregion501502// # Used definitions503//504// ## User control levels505//506// chain-controlled - key is uncontrolled by user507//                    i.e autoincrementing index508//                    can use non-cryptographic hash509// real - key is controlled by user510//        but it is hard to generate enough colliding values, i.e owner of signed txs511//        can use non-cryptographic hash512// controlled - key is completly controlled by users513//              i.e maps with mutable keys514//              should use cryptographic hash515//516// ## User control level downgrade reasons517//518// ?1 - chain-controlled -> controlled519//      collections/tokens can be destroyed, resulting in massive holes520// ?2 - chain-controlled -> controlled521//      same as ?1, but can be only added, resulting in easier exploitation522// ?3 - real -> controlled523//      no confirmation required, so addresses can be easily generated524decl_storage! {525    trait Store for Module<T: Config> as Nft {526527        //#region Private members528        /// Id of next collection529        CreatedCollectionCount: u32;530        /// Used for migrations531        ChainVersion: u64;532        /// Id of last collection token533        /// Collection id (controlled?1)534        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;535        //#endregion536537        //#region Chain limits struct538        pub ChainLimit get(fn chain_limit) config(): ChainLimits;539        //#endregion540541        //#region Bound counters542        /// Amount of collections destroyed, used for total amount tracking with543        /// CreatedCollectionCount544        DestroyedCollectionCount: u32;545        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)546        /// Account id (real)547        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;548        //#endregion549550        //#region Basic collections551        /// Collection info552        /// Collection id (controlled?1)553        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;554        /// List of collection admins555        /// Collection id (controlled?2)556        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;557        /// Whitelisted collection users558        /// Collection id (controlled?2), user id (controlled?3)559        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;560        //#endregion561562        /// How many of collection items user have563        /// Collection id (controlled?2), account id (real)564        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;565566        /// Amount of items which spender can transfer out of owners account (via transferFrom)567        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))568        /// TODO: Off chain worker should remove from this map when token gets removed569        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;570571        //#region Item collections572        /// Collection id (controlled?2), token id (controlled?1)573        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;574        /// Collection id (controlled?2), owner (controlled?2)575        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;576        /// Collection id (controlled?2), token id (controlled?1)577        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;578        //#endregion579580        //#region Index list581        /// Collection id (controlled?2), tokens owner (controlled?2)582        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;583        //#endregion584585        //#region Tokens transfer rate limit baskets586        /// (Collection id (controlled?2), who created (real))587        /// TODO: Off chain worker should remove from this map when collection gets removed588        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;589        /// Collection id (controlled?2), token id (controlled?2)590        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;591        /// Collection id (controlled?2), owning user (real)592        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;593        /// Collection id (controlled?2), token id (controlled?2)594        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;595        //#endregion596597        /// Variable metadata sponsoring598        /// Collection id (controlled?2), token id (controlled?2)599        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;600      601        //#region Contract Sponsorship and Ownership602        /// Contract address (real)603        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;604        /// Contract address (real)605        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;606        /// (Contract address(real), caller (real))607        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;608        /// Contract address (real)609        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;610        /// Contract address (real)611        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 612        /// Contract address (real) => Whitelisted user (controlled?3)613        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 614        //#endregion615    }616    add_extra_genesis {617        build(|config: &GenesisConfig<T>| {618            // Modification of storage619            for (_num, _c) in &config.collection_id {620                <Module<T>>::init_collection(_c);621            }622623            for (_num, _c, _i) in &config.nft_item_id {624                <Module<T>>::init_nft_token(*_c, _i);625            }626627            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {628                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);629            }630631            for (_num, _c, _i) in &config.refungible_item_id {632                <Module<T>>::init_refungible_token(*_c, _i);633            }634        })635    }636}637638decl_event!(639    pub enum Event<T>640    where641        CrossAccountId = <T as Config>::CrossAccountId,642    {643        /// New collection was created644        /// 645        /// # Arguments646        /// 647        /// * collection_id: Globally unique identifier of newly created collection.648        /// 649        /// * mode: [CollectionMode] converted into u8.650        /// 651        /// * account_id: Collection owner.652        CollectionCreated(CollectionId, u8, CrossAccountId),653654        /// New item was created.655        /// 656        /// # Arguments657        /// 658        /// * collection_id: Id of the collection where item was created.659        /// 660        /// * item_id: Id of an item. Unique within the collection.661        ///662        /// * recipient: Owner of newly created item 663        ItemCreated(CollectionId, TokenId, CrossAccountId),664665        /// Collection item was burned.666        /// 667        /// # Arguments668        /// 669        /// collection_id.670        /// 671        /// item_id: Identifier of burned NFT.672        ItemDestroyed(CollectionId, TokenId),673674        /// Item was transferred675        ///676        /// * collection_id: Id of collection to which item is belong677        ///678        /// * item_id: Id of an item679        ///680        /// * sender: Original owner of item681        ///682        /// * recipient: New owner of item683        ///684        /// * amount: Always 1 for NFT685        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),686687        /// * collection_id688        ///689        /// * item_id690        ///691        /// * sender692        ///693        /// * spender694        ///695        /// * amount696        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),697    }698);699700decl_module! {701    pub struct Module<T: Config> for enum Call 702    where 703        origin: T::Origin704    {705        fn deposit_event() = default;706        type Error = Error<T>;707708        fn on_initialize(now: T::BlockNumber) -> Weight {709            0710        }711712        /// 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.713        /// 714        /// # Permissions715        /// 716        /// * Anyone.717        /// 718        /// # Arguments719        /// 720        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.721        /// 722        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.723        /// 724        /// * token_prefix: UTF-8 string with token prefix.725        /// 726        /// * mode: [CollectionMode] collection type and type dependent data.727        // returns collection ID728        #[weight = <T as Config>::WeightInfo::create_collection()]729        #[transactional]730        pub fn create_collection(origin,731                                 collection_name: Vec<u16>,732                                 collection_description: Vec<u16>,733                                 token_prefix: Vec<u8>,734                                 mode: CollectionMode) -> DispatchResult {735736            // Anyone can create a collection737            let who = T::CrossAccountId::from_sub(ensure_signed(origin)?);738739            // Take a (non-refundable) deposit of collection creation740            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();741            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(742                &T::TreasuryAccountId::get(),743                T::CollectionCreationPrice::get(),744            ));745            <T as Config>::Currency::settle(746                who.as_sub(),747                imbalance,748                WithdrawReasons::TRANSFER,749                ExistenceRequirement::KeepAlive,750            ).map_err(|_| Error::<T>::NoPermission)?;751752            let decimal_points = match mode {753                CollectionMode::Fungible(points) => points,754                _ => 0755            };756757            let chain_limit = ChainLimit::get();758759            let created_count = CreatedCollectionCount::get();760            let destroyed_count = DestroyedCollectionCount::get();761762            // bound Total number of collections763            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);764765            // check params766            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);767            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);768            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);769            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);770771            // Generate next collection ID772            let next_id = created_count773                .checked_add(1)774                .ok_or(Error::<T>::NumOverflow)?;775776            CreatedCollectionCount::put(next_id);777778            let limits = CollectionLimits {779                sponsored_data_size: chain_limit.custom_data_limit,780                ..Default::default()781            };782783            // Create new collection784            let new_collection = Collection {785                owner: who.clone(),786                name: collection_name,787                mode: mode.clone(),788                mint_mode: false,789                access: AccessMode::Normal,790                description: collection_description,791                decimal_points: decimal_points,792                token_prefix: token_prefix,793                offchain_schema: Vec::new(),794                schema_version: SchemaVersion::ImageURL,795                sponsorship: SponsorshipState::Disabled,796                variable_on_chain_schema: Vec::new(),797                const_on_chain_schema: Vec::new(),798                limits,799            };800801            // Add new collection to map802            <CollectionById<T>>::insert(next_id, new_collection);803804            // call event805            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));806807            Ok(())808        }809810        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.811        /// 812        /// # Permissions813        /// 814        /// * Collection Owner.815        /// 816        /// # Arguments817        /// 818        /// * collection_id: collection to destroy.819        #[weight = <T as Config>::WeightInfo::destroy_collection()]820        #[transactional]821        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {822823            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);824            let collection = Self::get_collection(collection_id)?;825            Self::check_owner_permissions(&collection, &sender)?;826            if !collection.limits.owner_can_destroy {827                fail!(Error::<T>::NoPermission);828            }829830            <AddressTokens<T>>::remove_prefix(collection_id);831            <Allowances<T>>::remove_prefix(collection_id);832            <Balance<T>>::remove_prefix(collection_id);833            <ItemListIndex>::remove(collection_id);834            <AdminList<T>>::remove(collection_id);835            <CollectionById<T>>::remove(collection_id);836            <WhiteList<T>>::remove_prefix(collection_id);837838            <NftItemList<T>>::remove_prefix(collection_id);839            <FungibleItemList<T>>::remove_prefix(collection_id);840            <ReFungibleItemList<T>>::remove_prefix(collection_id);841842            <NftTransferBasket<T>>::remove_prefix(collection_id);843            <FungibleTransferBasket<T>>::remove_prefix(collection_id);844            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);845846            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);847848            DestroyedCollectionCount::put(DestroyedCollectionCount::get()849                .checked_add(1)850                .ok_or(Error::<T>::NumOverflow)?);851852            Ok(())853        }854855        /// Add an address to white list.856        /// 857        /// # Permissions858        /// 859        /// * Collection Owner860        /// * Collection Admin861        /// 862        /// # Arguments863        /// 864        /// * collection_id.865        /// 866        /// * address.867        #[weight = <T as Config>::WeightInfo::add_to_white_list()]868        #[transactional]869        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{870871            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);872            let collection = Self::get_collection(collection_id)?;873874            Self::toggle_white_list_internal(875                &sender,876                &collection,877                &address,878                true,879            )?;880881            Ok(())882        }883884        /// Remove an address from white list.885        /// 886        /// # Permissions887        /// 888        /// * Collection Owner889        /// * Collection Admin890        /// 891        /// # Arguments892        /// 893        /// * collection_id.894        /// 895        /// * address.896        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]897        #[transactional]898        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{899900            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);901            let collection = Self::get_collection(collection_id)?;902903            Self::toggle_white_list_internal(904                &sender,905                &collection,906                &address,907                false,908            )?;909910            Ok(())911        }912913        /// Toggle between normal and white list access for the methods with access for `Anyone`.914        /// 915        /// # Permissions916        /// 917        /// * Collection Owner.918        /// 919        /// # Arguments920        /// 921        /// * collection_id.922        /// 923        /// * mode: [AccessMode]924        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]925        #[transactional]926        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult927        {928            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);929930            let mut target_collection = Self::get_collection(collection_id)?;931            Self::check_owner_permissions(&target_collection, &sender)?;932            target_collection.access = mode;933            Self::save_collection(target_collection);934935            Ok(())936        }937938        /// Allows Anyone to create tokens if:939        /// * White List is enabled, and940        /// * Address is added to white list, and941        /// * This method was called with True parameter942        /// 943        /// # Permissions944        /// * Collection Owner945        ///946        /// # Arguments947        /// 948        /// * collection_id.949        /// 950        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.951        #[weight = <T as Config>::WeightInfo::set_mint_permission()]952        #[transactional]953        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult954        {955            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);956957            let mut target_collection = Self::get_collection(collection_id)?;958            Self::check_owner_permissions(&target_collection, &sender)?;959            target_collection.mint_mode = mint_permission;960            Self::save_collection(target_collection);961962            Ok(())963        }964965        /// Change the owner of the collection.966        /// 967        /// # Permissions968        /// 969        /// * Collection Owner.970        /// 971        /// # Arguments972        /// 973        /// * collection_id.974        /// 975        /// * new_owner.976        #[weight = <T as Config>::WeightInfo::change_collection_owner()]977        #[transactional]978        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::CrossAccountId) -> DispatchResult {979980            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);981            let mut target_collection = Self::get_collection(collection_id)?;982            Self::check_owner_permissions(&target_collection, &sender)?;983            target_collection.owner = new_owner;984            Self::save_collection(target_collection);985986            Ok(())987        }988989        /// Adds an admin of the Collection.990        /// 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. 991        /// 992        /// # Permissions993        /// 994        /// * Collection Owner.995        /// * Collection Admin.996        /// 997        /// # Arguments998        /// 999        /// * collection_id: ID of the Collection to add admin for.1000        /// 1001        /// * new_admin_id: Address of new admin to add.1002        #[weight = <T as Config>::WeightInfo::add_collection_admin()]1003        #[transactional]1004        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1005            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1006            let collection = Self::get_collection(collection_id)?;1007            Self::check_owner_or_admin_permissions(&collection, &sender)?;1008            let mut admin_arr = <AdminList<T>>::get(collection_id);10091010            match admin_arr.binary_search(&new_admin_id) {1011                Ok(_) => {},1012                Err(idx) => {1013                    let limits = ChainLimit::get();1014                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1015                    admin_arr.insert(idx, new_admin_id);1016                    <AdminList<T>>::insert(collection_id, admin_arr);1017                }1018            }1019            Ok(())1020        }10211022        /// 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.1023        ///1024        /// # Permissions1025        /// 1026        /// * Collection Owner.1027        /// * Collection Admin.1028        /// 1029        /// # Arguments1030        /// 1031        /// * collection_id: ID of the Collection to remove admin for.1032        /// 1033        /// * account_id: Address of admin to remove.1034        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1035        #[transactional]1036        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1037            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1038            let collection = Self::get_collection(collection_id)?;1039            Self::check_owner_or_admin_permissions(&collection, &sender)?;1040            let mut admin_arr = <AdminList<T>>::get(collection_id);10411042            match admin_arr.binary_search(&account_id) {1043                Ok(idx) => {1044                    admin_arr.remove(idx);1045                    <AdminList<T>>::insert(collection_id, admin_arr);1046                },1047                Err(_) => {}1048            }1049            Ok(())1050        }10511052        /// # Permissions1053        /// 1054        /// * Collection Owner1055        /// 1056        /// # Arguments1057        /// 1058        /// * collection_id.1059        /// 1060        /// * new_sponsor.1061        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1062        #[transactional]1063        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1064            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1065            let mut target_collection = Self::get_collection(collection_id)?;1066            Self::check_owner_permissions(&target_collection, &sender)?;10671068            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1069            Self::save_collection(target_collection);10701071            Ok(())1072        }10731074        /// # Permissions1075        /// 1076        /// * Sponsor.1077        /// 1078        /// # Arguments1079        /// 1080        /// * collection_id.1081        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1082        #[transactional]1083        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1084            let sender = ensure_signed(origin)?;10851086            let mut target_collection = Self::get_collection(collection_id)?;1087            ensure!(1088                target_collection.sponsorship.pending_sponsor() == Some(&sender),1089                Error::<T>::ConfirmUnsetSponsorFail1090            );10911092            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1093            Self::save_collection(target_collection);10941095            Ok(())1096        }10971098        /// Switch back to pay-per-own-transaction model.1099        ///1100        /// # Permissions1101        ///1102        /// * Collection owner.1103        /// 1104        /// # Arguments1105        /// 1106        /// * collection_id.1107        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1108        #[transactional]1109        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1110            let sender = ensure_signed(origin)?;11111112            let mut target_collection = Self::get_collection(collection_id)?;1113            Self::check_owner_permissions(&target_collection, &T::CrossAccountId::from_sub(sender))?;11141115            target_collection.sponsorship = SponsorshipState::Disabled;1116            Self::save_collection(target_collection);11171118            Ok(())1119        }11201121        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1122        /// 1123        /// # Permissions1124        /// 1125        /// * Collection Owner.1126        /// * Collection Admin.1127        /// * Anyone if1128        ///     * White List is enabled, and1129        ///     * Address is added to white list, and1130        ///     * MintPermission is enabled (see SetMintPermission method)1131        /// 1132        /// # Arguments1133        /// 1134        /// * collection_id: ID of the collection.1135        /// 1136        /// * owner: Address, initial owner of the NFT.1137        ///1138        /// * data: Token data to store on chain.1139        // #[weight =1140        // (130_000_000 as Weight)1141        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1142        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1143        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11441145        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1146        #[transactional]1147        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1148            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1149            let collection = Self::get_collection(collection_id)?;11501151            Self::create_item_internal(&sender, &collection, &owner, data)?;11521153            Self::submit_logs(collection)?;1154            Ok(())1155        }11561157        /// This method creates multiple items in a collection created with CreateCollection method.1158        /// 1159        /// # Permissions1160        /// 1161        /// * Collection Owner.1162        /// * Collection Admin.1163        /// * Anyone if1164        ///     * White List is enabled, and1165        ///     * Address is added to white list, and1166        ///     * MintPermission is enabled (see SetMintPermission method)1167        /// 1168        /// # Arguments1169        /// 1170        /// * collection_id: ID of the collection.1171        /// 1172        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1173        /// 1174        /// * owner: Address, initial owner of the NFT.1175        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1176                               .map(|data| { data.len() })1177                               .sum())]1178        #[transactional]1179        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11801181            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1182            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1183            let collection = Self::get_collection(collection_id)?;11841185            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11861187            Self::submit_logs(collection)?;1188            Ok(())1189        }11901191        /// Destroys a concrete instance of NFT.1192        /// 1193        /// # Permissions1194        /// 1195        /// * Collection Owner.1196        /// * Collection Admin.1197        /// * Current NFT Owner.1198        /// 1199        /// # Arguments1200        /// 1201        /// * collection_id: ID of the collection.1202        /// 1203        /// * item_id: ID of NFT to burn.1204        #[weight = <T as Config>::WeightInfo::burn_item()]1205        #[transactional]1206        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12071208            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1209            let target_collection = Self::get_collection(collection_id)?;12101211            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12121213            Self::submit_logs(target_collection)?;1214            Ok(())1215        }12161217        /// Change ownership of the token.1218        /// 1219        /// # Permissions1220        /// 1221        /// * Collection Owner1222        /// * Collection Admin1223        /// * Current NFT owner1224        ///1225        /// # Arguments1226        /// 1227        /// * recipient: Address of token recipient.1228        /// 1229        /// * collection_id.1230        /// 1231        /// * item_id: ID of the item1232        ///     * Non-Fungible Mode: Required.1233        ///     * Fungible Mode: Ignored.1234        ///     * Re-Fungible Mode: Required.1235        /// 1236        /// * value: Amount to transfer.1237        ///     * Non-Fungible Mode: Ignored1238        ///     * Fungible Mode: Must specify transferred amount1239        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1240        #[weight = <T as Config>::WeightInfo::transfer()]1241        #[transactional]1242        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1243            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1244            let collection = Self::get_collection(collection_id)?;12451246            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12471248            Self::submit_logs(collection)?;1249            Ok(())1250        }12511252        /// Set, change, or remove approved address to transfer the ownership of the NFT.1253        /// 1254        /// # Permissions1255        /// 1256        /// * Collection Owner1257        /// * Collection Admin1258        /// * Current NFT owner1259        /// 1260        /// # Arguments1261        /// 1262        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1263        /// 1264        /// * collection_id.1265        /// 1266        /// * item_id: ID of the item.1267        #[weight = <T as Config>::WeightInfo::approve()]1268        #[transactional]1269        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1270            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1271            let collection = Self::get_collection(collection_id)?;12721273            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12741275            Self::submit_logs(collection)?;1276            Ok(())1277        }1278        1279        /// 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.1280        /// 1281        /// # Permissions1282        /// * Collection Owner1283        /// * Collection Admin1284        /// * Current NFT owner1285        /// * Address approved by current NFT owner1286        /// 1287        /// # Arguments1288        /// 1289        /// * from: Address that owns token.1290        /// 1291        /// * recipient: Address of token recipient.1292        /// 1293        /// * collection_id.1294        /// 1295        /// * item_id: ID of the item.1296        /// 1297        /// * value: Amount to transfer.1298        #[weight = <T as Config>::WeightInfo::transfer_from()]1299        #[transactional]1300        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1301            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1302            let collection = Self::get_collection(collection_id)?;13031304            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13051306            Self::submit_logs(collection)?;1307            Ok(())1308        }1309        // #[weight = 0]1310        //     // let no_perm_mes = "You do not have permissions to modify this collection";1311        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1312        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1313        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13141315        //     // // on_nft_received  call13161317        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13181319        //     Ok(())1320        // }13211322        /// Set off-chain data schema.1323        /// 1324        /// # Permissions1325        /// 1326        /// * Collection Owner1327        /// * Collection Admin1328        /// 1329        /// # Arguments1330        /// 1331        /// * collection_id.1332        /// 1333        /// * schema: String representing the offchain data schema.1334        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1335        #[transactional]1336        pub fn set_variable_meta_data (1337            origin,1338            collection_id: CollectionId,1339            item_id: TokenId,1340            data: Vec<u8>1341        ) -> DispatchResult {1342            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1343            1344            let collection = Self::get_collection(collection_id)?;13451346            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13471348            Ok(())1349        }1350 1351        /// Set schema standard1352        /// ImageURL1353        /// Unique1354        /// 1355        /// # Permissions1356        /// 1357        /// * Collection Owner1358        /// * Collection Admin1359        /// 1360        /// # Arguments1361        /// 1362        /// * collection_id.1363        /// 1364        /// * schema: SchemaVersion: enum1365        #[weight = <T as Config>::WeightInfo::set_schema_version()]1366        #[transactional]1367        pub fn set_schema_version(1368            origin,1369            collection_id: CollectionId,1370            version: SchemaVersion1371        ) -> DispatchResult {1372            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1373            let mut target_collection = Self::get_collection(collection_id)?;1374            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1375            target_collection.schema_version = version;1376            Self::save_collection(target_collection);13771378            Ok(())1379        }13801381        /// Set off-chain data schema.1382        /// 1383        /// # Permissions1384        /// 1385        /// * Collection Owner1386        /// * Collection Admin1387        /// 1388        /// # Arguments1389        /// 1390        /// * collection_id.1391        /// 1392        /// * schema: String representing the offchain data schema.1393        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1394        #[transactional]1395        pub fn set_offchain_schema(1396            origin,1397            collection_id: CollectionId,1398            schema: Vec<u8>1399        ) -> DispatchResult {1400            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1401            let mut target_collection = Self::get_collection(collection_id)?;1402            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14031404            // check schema limit1405            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14061407            target_collection.offchain_schema = schema;1408            Self::save_collection(target_collection);14091410            Ok(())1411        }14121413        /// Set const on-chain data schema.1414        /// 1415        /// # Permissions1416        /// 1417        /// * Collection Owner1418        /// * Collection Admin1419        /// 1420        /// # Arguments1421        /// 1422        /// * collection_id.1423        /// 1424        /// * schema: String representing the const on-chain data schema.1425        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1426        #[transactional]1427        pub fn set_const_on_chain_schema (1428            origin,1429            collection_id: CollectionId,1430            schema: Vec<u8>1431        ) -> DispatchResult {1432            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1433            let mut target_collection = Self::get_collection(collection_id)?;1434            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14351436            // check schema limit1437            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14381439            target_collection.const_on_chain_schema = schema;1440            Self::save_collection(target_collection);14411442            Ok(())1443        }14441445        /// Set variable on-chain data schema.1446        /// 1447        /// # Permissions1448        /// 1449        /// * Collection Owner1450        /// * Collection Admin1451        /// 1452        /// # Arguments1453        /// 1454        /// * collection_id.1455        /// 1456        /// * schema: String representing the variable on-chain data schema.1457        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1458        #[transactional]1459        pub fn set_variable_on_chain_schema (1460            origin,1461            collection_id: CollectionId,1462            schema: Vec<u8>1463        ) -> DispatchResult {1464            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1465            let mut target_collection = Self::get_collection(collection_id)?;1466            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14671468            // check schema limit1469            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14701471            target_collection.variable_on_chain_schema = schema;1472            Self::save_collection(target_collection);14731474            Ok(())1475        }14761477        // Sudo permissions function1478        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1479        #[transactional]1480        pub fn set_chain_limits(1481            origin,1482            limits: ChainLimits1483        ) -> DispatchResult {14841485            #[cfg(not(feature = "runtime-benchmarks"))]1486            ensure_root(origin)?;14871488            <ChainLimit>::put(limits);1489            Ok(())1490        }14911492        /// Enable smart contract self-sponsoring.1493        /// 1494        /// # Permissions1495        /// 1496        /// * Contract Owner1497        /// 1498        /// # Arguments1499        /// 1500        /// * contract address1501        /// * enable flag1502        /// 1503        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1504        #[transactional]1505        pub fn enable_contract_sponsoring(1506            origin,1507            contract_address: T::AccountId,1508            enable: bool1509        ) -> DispatchResult {15101511            let sender = ensure_signed(origin)?;15121513            #[cfg(feature = "runtime-benchmarks")]1514            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15151516            Self::ensure_contract_owned(sender, &contract_address)?;15171518            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1519            Ok(())1520        }15211522        /// Set the rate limit for contract sponsoring to specified number of blocks.1523        /// 1524        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1525        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1526        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1527        /// from contract endowment if there are at least B blocks between such transactions. 1528        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1529        /// 1530        /// # Permissions1531        /// 1532        /// * Contract Owner1533        /// 1534        /// # Arguments1535        /// 1536        /// -`contract_address`: Address of the contract to sponsor1537        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1538        /// 1539        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1540        #[transactional]1541        pub fn set_contract_sponsoring_rate_limit(1542            origin,1543            contract_address: T::AccountId,1544            rate_limit: T::BlockNumber1545        ) -> DispatchResult {1546            let sender = ensure_signed(origin)?;15471548            #[cfg(feature = "runtime-benchmarks")]1549            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15501551            Self::ensure_contract_owned(sender, &contract_address)?;1552            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1553            Ok(())1554        }15551556        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1557        /// 1558        /// # Permissions1559        /// 1560        /// * Address that deployed smart contract.1561        /// 1562        /// # Arguments1563        /// 1564        /// -`contract_address`: Address of the contract.1565        /// 1566        /// - `enable`: .  1567        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1568        #[transactional]1569        pub fn toggle_contract_white_list(1570            origin,1571            contract_address: T::AccountId,1572            enable: bool1573        ) -> DispatchResult {1574            let sender = ensure_signed(origin)?;15751576            #[cfg(feature = "runtime-benchmarks")]1577            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15781579            Self::ensure_contract_owned(sender, &contract_address)?;1580            if enable {1581                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1582            } else {1583                <ContractWhiteListEnabled<T>>::remove(contract_address);1584            }1585            Ok(())1586        }1587        1588        /// Add an address to smart contract white list.1589        /// 1590        /// # Permissions1591        /// 1592        /// * Address that deployed smart contract.1593        /// 1594        /// # Arguments1595        /// 1596        /// -`contract_address`: Address of the contract.1597        ///1598        /// -`account_address`: Address to add.1599        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1600        #[transactional]1601        pub fn add_to_contract_white_list(1602            origin,1603            contract_address: T::AccountId,1604            account_address: T::AccountId1605        ) -> DispatchResult {1606            let sender = ensure_signed(origin)?;16071608            #[cfg(feature = "runtime-benchmarks")]1609            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1610            1611            Self::ensure_contract_owned(sender, &contract_address)?;      1612            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1613            Ok(())1614        }16151616        /// Remove an address from smart contract white list.1617        /// 1618        /// # Permissions1619        /// 1620        /// * Address that deployed smart contract.1621        /// 1622        /// # Arguments1623        /// 1624        /// -`contract_address`: Address of the contract.1625        ///1626        /// -`account_address`: Address to remove.1627        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1628        #[transactional]1629        pub fn remove_from_contract_white_list(1630            origin,1631            contract_address: T::AccountId,1632            account_address: T::AccountId1633        ) -> DispatchResult {1634            let sender = ensure_signed(origin)?;16351636            #[cfg(feature = "runtime-benchmarks")]1637            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16381639            Self::ensure_contract_owned(sender, &contract_address)?;1640            <ContractWhiteList<T>>::remove(contract_address, account_address);1641            Ok(())1642        }16431644        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1645        #[transactional]1646        pub fn set_collection_limits(1647            origin,1648            collection_id: u32,1649            new_limits: CollectionLimits<T::BlockNumber>,1650        ) -> DispatchResult {1651            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1652            let mut target_collection = Self::get_collection(collection_id)?;1653            Self::check_owner_permissions(&target_collection, &sender)?;1654            let old_limits = &target_collection.limits;1655            let chain_limits = ChainLimit::get();16561657            // collection bounds1658            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1659                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1660                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1661                Error::<T>::CollectionLimitBoundsExceeded);16621663            // token_limit   check  prev1664            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1665            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16661667            ensure!(1668                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1669                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1670                Error::<T>::OwnerPermissionsCantBeReverted,1671            );16721673            target_collection.limits = new_limits;1674            Self::save_collection(target_collection);16751676            Ok(())1677        } 1678    }1679}16801681impl<T: Config> Module<T> {1682    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1683        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1684        Self::validate_create_item_args(&collection, &data)?;1685        Self::create_item_no_validation(&collection, owner, data)?;16861687        Ok(())1688    }16891690    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1691        // Limits check1692        Self::is_correct_transfer(target_collection, &recipient)?;16931694        // Transfer permissions check1695        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1696            Self::is_owner_or_admin_permissions(target_collection, &sender),1697            Error::<T>::NoPermission);16981699        if target_collection.access == AccessMode::WhiteList {1700            Self::check_white_list(target_collection, &sender)?;1701            Self::check_white_list(target_collection, &recipient)?;1702        }17031704        match target_collection.mode1705        {1706            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1707            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1708            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1709            _ => ()1710        };17111712        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17131714        Ok(())1715    }17161717	pub fn approve_internal(1718		sender: &T::CrossAccountId,1719		spender: &T::CrossAccountId,1720		collection: &CollectionHandle<T>,1721		item_id: TokenId,1722		amount: u1281723	) -> DispatchResult {1724		Self::token_exists(&collection, item_id)?;17251726		// Transfer permissions check1727		let bypasses_limits = collection.limits.owner_can_transfer &&1728			Self::is_owner_or_admin_permissions(1729				&collection,1730				&sender,1731			);17321733		let allowance_limit = if bypasses_limits {1734			None1735		} else if let Some(amount) = Self::owned_amount(1736			&sender,1737			&collection,1738			item_id,1739		) {1740			Some(amount)1741		} else {1742			fail!(Error::<T>::NoPermission);1743		};17441745		if collection.access == AccessMode::WhiteList {1746			Self::check_white_list(&collection, &sender)?;1747			Self::check_white_list(&collection, &spender)?;1748		}17491750		let allowance: u128 = amount1751			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1752			.ok_or(Error::<T>::NumOverflow)?;1753		if let Some(limit) = allowance_limit {1754			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1755		}1756		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17571758		if matches!(collection.mode, CollectionMode::NFT) {1759			// TODO: NFT: only one owner may exist for token in ERC7211760			collection.log(1761				Vec::from([1762					eth::APPROVAL_NFT_TOPIC,1763					eth::address_to_topic(sender.as_eth()),1764					eth::address_to_topic(spender.as_eth()),1765				]),1766				abi_encode!(uint256(item_id.into())),1767			);1768		}17691770		if matches!(collection.mode, CollectionMode::Fungible(_)) {1771			// TODO: NFT: only one owner may exist for token in ERC201772			collection.log(1773				Vec::from([1774					eth::APPROVAL_FUNGIBLE_TOPIC,1775					eth::address_to_topic(sender.as_eth()),1776					eth::address_to_topic(spender.as_eth()),1777				]),1778				abi_encode!(uint256(allowance.into())),1779			);1780		}17811782		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1783		Ok(())1784	}17851786	pub fn transfer_from_internal(1787		sender: &T::CrossAccountId,1788		from: &T::CrossAccountId,1789		recipient: &T::CrossAccountId,1790		collection: &CollectionHandle<T>,1791		item_id: TokenId,1792		amount: u128,1793	) -> DispatchResult {1794		// Check approval1795		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17961797		// Limits check1798		Self::is_correct_transfer(&collection, &recipient)?;17991800		// Transfer permissions check1801		ensure!(1802			approval >= amount || 1803			(1804				collection.limits.owner_can_transfer &&1805				Self::is_owner_or_admin_permissions(&collection, &sender)1806			),1807			Error::<T>::NoPermission1808		);18091810		if collection.access == AccessMode::WhiteList {1811			Self::check_white_list(&collection, &sender)?;1812			Self::check_white_list(&collection, &recipient)?;1813		}18141815		// Reduce approval by transferred amount or remove if remaining approval drops to 01816		let allowance = approval.saturating_sub(amount);1817		if allowance > 0 {1818			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1819		} else {1820			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1821		}18221823		match collection.mode {1824			CollectionMode::NFT => {1825				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1826			}1827			CollectionMode::Fungible(_) => {1828				Self::transfer_fungible(&collection, amount, &from, &recipient)?1829			}1830			CollectionMode::ReFungible => {1831				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1832			}1833			_ => ()1834		};18351836		if matches!(collection.mode, CollectionMode::Fungible(_)) {1837			collection.log(1838				Vec::from([1839					eth::APPROVAL_FUNGIBLE_TOPIC,1840					eth::address_to_topic(from.as_eth()),1841					eth::address_to_topic(sender.as_eth()),1842				]),1843				abi_encode!(uint256(allowance.into())),1844			);1845		}18461847		Ok(())1848	}18491850    pub fn set_variable_meta_data_internal(1851        sender: &T::CrossAccountId,1852        collection: &CollectionHandle<T>, 1853        item_id: TokenId,1854        data: Vec<u8>,1855    ) -> DispatchResult {1856        Self::token_exists(&collection, item_id)?;18571858        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18591860        // Modify permissions check1861        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1862            Self::is_owner_or_admin_permissions(&collection, &sender),1863            Error::<T>::NoPermission);18641865        match collection.mode1866        {1867            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1868            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1869            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1870            _ => fail!(Error::<T>::UnexpectedCollectionType)1871        };18721873        Ok(())1874    }18751876    pub fn create_multiple_items_internal(1877        sender: &T::CrossAccountId,1878        collection: &CollectionHandle<T>,1879        owner: &T::CrossAccountId,1880        items_data: Vec<CreateItemData>,1881    ) -> DispatchResult {1882        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18831884        for data in &items_data {1885            Self::validate_create_item_args(&collection, data)?;1886        }1887        for data in &items_data {1888            Self::create_item_no_validation(&collection, owner, data.clone())?;1889        }18901891        Ok(())1892    }18931894    pub fn burn_item_internal(1895        sender: &T::CrossAccountId,1896        collection: &CollectionHandle<T>,1897        item_id: TokenId,1898        value: u128,1899    ) -> DispatchResult {1900        ensure!(1901            Self::is_item_owner(&sender, &collection, item_id) ||1902            (1903                collection.limits.owner_can_transfer &&1904                Self::is_owner_or_admin_permissions(&collection, &sender)1905            ),1906            Error::<T>::NoPermission1907        );19081909        if collection.access == AccessMode::WhiteList {1910            Self::check_white_list(&collection, &sender)?;1911        }19121913        match collection.mode1914        {1915            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1916            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1917            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1918            _ => ()1919        };19201921        Ok(())1922    }19231924    pub fn toggle_white_list_internal(1925        sender: &T::CrossAccountId,1926        collection: &CollectionHandle<T>,1927        address: &T::CrossAccountId,1928        whitelisted: bool,1929    ) -> DispatchResult {1930        Self::check_owner_or_admin_permissions(&collection, &sender)?;19311932        if whitelisted {1933            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1934        } else {1935            <WhiteList<T>>::remove(collection.id, address.as_sub());1936        }19371938        Ok(())1939    }19401941    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1942        let collection_id = collection.id;19431944        // check token limit and account token limit1945        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1946        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1947        1948        Ok(())1949    }19501951    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1952        let collection_id = collection.id;19531954        // check token limit and account token limit1955        let total_items: u32 = ItemListIndex::get(collection_id)1956            .checked_add(amount)1957            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1958        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1959            .checked_add(amount)1960            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1961        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1962        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19631964        if !Self::is_owner_or_admin_permissions(collection, &sender) {1965            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1966            Self::check_white_list(collection, owner)?;1967            Self::check_white_list(collection, sender)?;1968        }19691970        Ok(())1971    }19721973    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1974        match target_collection.mode1975        {1976            CollectionMode::NFT => {1977                if let CreateItemData::NFT(data) = data {1978                    // check sizes1979                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1980                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1981                } else {1982                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1983                }1984            },1985            CollectionMode::Fungible(_) => {1986                if let CreateItemData::Fungible(_) = data {1987                } else {1988                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1989                }1990            },1991            CollectionMode::ReFungible => {1992                if let CreateItemData::ReFungible(data) = data {19931994                    // check sizes1995                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1996                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19971998                    // Check refungibility limits1999                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);2000                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);2001                } else {2002                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);2003                }2004            },2005            _ => { fail!(Error::<T>::UnexpectedCollectionType); }2006        };20072008        Ok(())2009    }20102011    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2012        match data2013        {2014            CreateItemData::NFT(data) => {2015                let item = NftItemType {2016                    owner: owner.clone(),2017                    const_data: data.const_data,2018                    variable_data: data.variable_data2019                };20202021                Self::add_nft_item(collection, item)?;2022            },2023            CreateItemData::Fungible(data) => {2024                Self::add_fungible_item(collection, &owner, data.value)?;2025            },2026            CreateItemData::ReFungible(data) => {2027                let mut owner_list = Vec::new();2028                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20292030                let item = ReFungibleItemType {2031                    owner: owner_list,2032                    const_data: data.const_data,2033                    variable_data: data.variable_data2034                };20352036                Self::add_refungible_item(collection, item)?;2037            }2038        };20392040        Ok(())2041    }20422043    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2044        let collection_id = collection.id;20452046        // Does new owner already have an account?2047        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20482049        // Mint 2050        let item = FungibleItemType {2051            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2052        };2053        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20542055        // Update balance2056        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2057            .checked_add(value)2058            .ok_or(Error::<T>::NumOverflow)?;2059        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20602061        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2062        Ok(())2063    }20642065    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2066        let collection_id = collection.id;20672068        let current_index = <ItemListIndex>::get(collection_id)2069            .checked_add(1)2070            .ok_or(Error::<T>::NumOverflow)?;2071        let itemcopy = item.clone();20722073        ensure!(2074            item.owner.len() == 1,2075            Error::<T>::BadCreateRefungibleCall,2076        );2077        let item_owner = item.owner.first().expect("only one owner is defined");20782079        let value = item_owner.fraction;2080        let owner = item_owner.owner.clone();20812082        Self::add_token_index(collection_id, current_index, &owner)?;20832084        <ItemListIndex>::insert(collection_id, current_index);2085        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20862087        // Update balance2088        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2089            .checked_add(value)2090            .ok_or(Error::<T>::NumOverflow)?;2091        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20922093        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2094        Ok(())2095    }20962097    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2098        let collection_id = collection.id;20992100        let current_index = <ItemListIndex>::get(collection_id)2101            .checked_add(1)2102            .ok_or(Error::<T>::NumOverflow)?;21032104        let item_owner = item.owner.clone();2105        Self::add_token_index(collection_id, current_index, &item.owner)?;21062107        <ItemListIndex>::insert(collection_id, current_index);2108        <NftItemList<T>>::insert(collection_id, current_index, item);21092110        // Update balance2111        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2112            .checked_add(1)2113            .ok_or(Error::<T>::NumOverflow)?;2114        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21152116        collection.log(2117            Vec::from([2118                eth::TRANSFER_NFT_TOPIC,2119                eth::address_to_topic(&H160::default()),2120                eth::address_to_topic(item_owner.as_eth()),2121            ]),2122            abi_encode!(uint256(current_index.into())),2123        );2124        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2125        Ok(())2126    }21272128    fn burn_refungible_item(2129        collection: &CollectionHandle<T>,2130        item_id: TokenId,2131        owner: &T::CrossAccountId,2132    ) -> DispatchResult {2133        let collection_id = collection.id;21342135        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2136            .ok_or(Error::<T>::TokenNotFound)?;2137        let rft_balance = token2138            .owner2139            .iter()2140            .find(|&i| i.owner == *owner)2141            .ok_or(Error::<T>::TokenNotFound)?;2142        Self::remove_token_index(collection_id, item_id, owner)?;21432144        // update balance2145        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2146            .checked_sub(rft_balance.fraction)2147            .ok_or(Error::<T>::NumOverflow)?;2148        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21492150        // Re-create owners list with sender removed2151        let index = token2152            .owner2153            .iter()2154            .position(|i| i.owner == *owner)2155            .expect("owned item is exists");2156        token.owner.remove(index);2157        let owner_count = token.owner.len();21582159        // Burn the token completely if this was the last (only) owner2160        if owner_count == 0 {2161            <ReFungibleItemList<T>>::remove(collection_id, item_id);2162            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2163        }2164        else {2165            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2166        }21672168        Ok(())2169    }21702171    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2172        let collection_id = collection.id;21732174        let item = <NftItemList<T>>::get(collection_id, item_id)2175            .ok_or(Error::<T>::TokenNotFound)?;2176        Self::remove_token_index(collection_id, item_id, &item.owner)?;21772178        // update balance2179        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2180            .checked_sub(1)2181            .ok_or(Error::<T>::NumOverflow)?;2182        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2183        <NftItemList<T>>::remove(collection_id, item_id);2184        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21852186        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2187        Ok(())2188    }21892190    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2191        let collection_id = collection.id;21922193        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2194        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21952196        // update balance2197        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2198            .checked_sub(value)2199            .ok_or(Error::<T>::NumOverflow)?;2200        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);22012202        if balance.value - value > 0 {2203            balance.value -= value;2204            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2205        }2206        else {2207            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2208        }22092210        collection.log(2211            Vec::from([2212                eth::TRANSFER_FUNGIBLE_TOPIC,2213                eth::address_to_topic(owner.as_eth()),2214                eth::address_to_topic(&H160::default()),2215            ]),2216            abi_encode!(uint256(value.into())),2217        );2218        Ok(())2219    }22202221    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2222        Ok(<CollectionHandle<T>>::get(collection_id)2223            .ok_or(Error::<T>::CollectionNotFound)?)2224    }22252226    fn save_collection(collection: CollectionHandle<T>) {2227        <CollectionById<T>>::insert(collection.id, collection.into_inner());2228    }22292230    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2231        if collection.logs.is_empty() {2232            return Ok(())2233        }2234        T::EthereumTransactionSender::submit_logs_transaction(2235            eth::generate_transaction(collection.id, T::EthereumChainId::get()),2236            collection.logs.retrieve_logs_for_contract(eth::collection_id_to_address(collection.id)),2237        )2238    }22392240    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> DispatchResult {2241        ensure!(2242            *subject == target_collection.owner,2243            Error::<T>::NoPermission2244        );22452246        Ok(())2247    }22482249    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2250        *subject == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2251    }22522253    fn check_owner_or_admin_permissions(2254        collection: &CollectionHandle<T>,2255        subject: &T::CrossAccountId,2256    ) -> DispatchResult {2257        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22582259        Ok(())2260    }22612262    fn owned_amount(2263        subject: &T::CrossAccountId,2264        target_collection: &CollectionHandle<T>,2265        item_id: TokenId,2266    ) -> Option<u128> {2267        let collection_id = target_collection.id;22682269        match target_collection.mode {2270            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2271                .then(|| 1),2272            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2273                .value),2274            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2275                .owner2276                .iter()2277                .find(|i| i.owner == *subject)2278                .map(|i| i.fraction),2279            CollectionMode::Invalid => None,2280        }2281    }22822283    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2284        match target_collection.mode {2285            CollectionMode::Fungible(_) => true,2286            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2287        }2288    }22892290    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2291        let collection_id = collection.id;22922293        let mes = Error::<T>::AddresNotInWhiteList;2294        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22952296        Ok(())2297    }22982299    /// Check if token exists. In case of Fungible, check if there is an entry for 2300    /// the owner in fungible balances double map2301    fn token_exists(2302        target_collection: &CollectionHandle<T>,2303        item_id: TokenId,2304    ) -> DispatchResult {2305        let collection_id = target_collection.id;2306        let exists = match target_collection.mode2307        {2308            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2309            CollectionMode::Fungible(_)  => true,2310            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2311            _ => false2312        };23132314        ensure!(exists == true, Error::<T>::TokenNotFound);2315        Ok(())2316    }23172318    fn transfer_fungible(2319        collection: &CollectionHandle<T>,2320        value: u128,2321        owner: &T::CrossAccountId,2322        recipient: &T::CrossAccountId,2323    ) -> DispatchResult {2324        let collection_id = collection.id;23252326        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2327        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23282329        // Send balance to recipient (updates balanceOf of recipient)2330        Self::add_fungible_item(collection, recipient, value)?;23312332        // update balanceOf of sender2333        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23342335        // Reduce or remove sender2336        if balance.value == value {2337            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2338        }2339        else {2340            balance.value -= value;2341            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2342        }23432344        collection.log(2345            Vec::from([2346                eth::TRANSFER_FUNGIBLE_TOPIC,2347                eth::address_to_topic(owner.as_eth()),2348                eth::address_to_topic(recipient.as_eth()),2349            ]),2350            abi_encode!(uint256(value.into())),2351        );2352        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23532354        Ok(())2355    }23562357    fn transfer_refungible(2358        collection: &CollectionHandle<T>,2359        item_id: TokenId,2360        value: u128,2361        owner: T::CrossAccountId,2362        new_owner: T::CrossAccountId,2363    ) -> DispatchResult {2364        let collection_id = collection.id;2365        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2366            .ok_or(Error::<T>::TokenNotFound)?;23672368        let item = full_item2369            .owner2370            .iter()2371            .filter(|i| i.owner == owner)2372            .next()2373            .ok_or(Error::<T>::TokenNotFound)?;2374        let amount = item.fraction;23752376        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23772378        // update balance2379        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2380            .checked_sub(value)2381            .ok_or(Error::<T>::NumOverflow)?;2382        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23832384        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2385            .checked_add(value)2386            .ok_or(Error::<T>::NumOverflow)?;2387        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23882389        let old_owner = item.owner.clone();2390        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23912392        // transfer2393        if amount == value && !new_owner_has_account {2394            // change owner2395            // new owner do not have account2396            let mut new_full_item = full_item.clone();2397            new_full_item2398                .owner2399                .iter_mut()2400                .find(|i| i.owner == owner)2401                .expect("old owner does present in refungible")2402                .owner = new_owner.clone();2403            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);24042405            // update index collection2406            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2407        } else {2408            let mut new_full_item = full_item.clone();2409            new_full_item2410                .owner2411                .iter_mut()2412                .find(|i| i.owner == owner)2413                .expect("old owner does present in refungible")2414                .fraction -= value;24152416            // separate amount2417            if new_owner_has_account {2418                // new owner has account2419                new_full_item2420                    .owner2421                    .iter_mut()2422                    .find(|i| i.owner == new_owner)2423                    .expect("new owner has account")2424                    .fraction += value;2425            } else {2426                // new owner do not have account2427                new_full_item.owner.push(Ownership {2428                    owner: new_owner.clone(),2429                    fraction: value,2430                });2431                Self::add_token_index(collection_id, item_id, &new_owner)?;2432            }24332434            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2435        }24362437        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24382439        Ok(())2440    }24412442    fn transfer_nft(2443        collection: &CollectionHandle<T>,2444        item_id: TokenId,2445        sender: T::CrossAccountId,2446        new_owner: T::CrossAccountId,2447    ) -> DispatchResult {2448        let collection_id = collection.id;2449        let mut item = <NftItemList<T>>::get(collection_id, item_id)2450            .ok_or(Error::<T>::TokenNotFound)?;24512452        ensure!(2453            sender == item.owner,2454            Error::<T>::MustBeTokenOwner2455        );24562457        // update balance2458        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2459            .checked_sub(1)2460            .ok_or(Error::<T>::NumOverflow)?;2461        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24622463        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2464            .checked_add(1)2465            .ok_or(Error::<T>::NumOverflow)?;2466        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24672468        // change owner2469        let old_owner = item.owner.clone();2470        item.owner = new_owner.clone();2471        <NftItemList<T>>::insert(collection_id, item_id, item);24722473        // update index collection2474        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24752476        collection.log(2477            Vec::from([2478                eth::TRANSFER_NFT_TOPIC,2479                eth::address_to_topic(sender.as_eth()),2480                eth::address_to_topic(new_owner.as_eth()),2481            ]),2482            abi_encode!(uint256(item_id.into())),2483        );2484        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24852486        Ok(())2487    }2488    2489    fn set_re_fungible_variable_data(2490        collection: &CollectionHandle<T>,2491        item_id: TokenId,2492        data: Vec<u8>2493    ) -> DispatchResult {2494        let collection_id = collection.id;2495        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2496            .ok_or(Error::<T>::TokenNotFound)?;24972498        item.variable_data = data;24992500        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);25012502        Ok(())2503    }25042505    fn set_nft_variable_data(2506        collection: &CollectionHandle<T>,2507        item_id: TokenId,2508        data: Vec<u8>2509    ) -> DispatchResult {2510        let collection_id = collection.id;2511        let mut item = <NftItemList<T>>::get(collection_id, item_id)2512            .ok_or(Error::<T>::TokenNotFound)?;2513        2514        item.variable_data = data;25152516        <NftItemList<T>>::insert(collection_id, item_id, item);2517        2518        Ok(())2519    }25202521    fn init_collection(item: &Collection<T>) {2522        // check params2523        assert!(2524            item.decimal_points <= MAX_DECIMAL_POINTS,2525            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2526        );2527        assert!(2528            item.name.len() <= 64,2529            "Collection name can not be longer than 63 char"2530        );2531        assert!(2532            item.name.len() <= 256,2533            "Collection description can not be longer than 255 char"2534        );2535        assert!(2536            item.token_prefix.len() <= 16,2537            "Token prefix can not be longer than 15 char"2538        );25392540        // Generate next collection ID2541        let next_id = CreatedCollectionCount::get()2542            .checked_add(1)2543            .unwrap();25442545        CreatedCollectionCount::put(next_id);2546    }25472548    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2549        let current_index = <ItemListIndex>::get(collection_id)2550            .checked_add(1)2551            .unwrap();25522553        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25542555        <ItemListIndex>::insert(collection_id, current_index);25562557        // Update balance2558        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2559            .checked_add(1)2560            .unwrap();2561        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2562    }25632564    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2565        let current_index = <ItemListIndex>::get(collection_id)2566            .checked_add(1)2567            .unwrap();25682569        Self::add_token_index(collection_id, current_index, owner).unwrap();25702571        <ItemListIndex>::insert(collection_id, current_index);25722573        // Update balance2574        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2575            .checked_add(item.value)2576            .unwrap();2577        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2578    }25792580    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2581        let current_index = <ItemListIndex>::get(collection_id)2582            .checked_add(1)2583            .unwrap();25842585        let value = item.owner.first().unwrap().fraction;2586        let owner = item.owner.first().unwrap().owner.clone();25872588        Self::add_token_index(collection_id, current_index, &owner).unwrap();25892590        <ItemListIndex>::insert(collection_id, current_index);25912592        // Update balance2593        let new_balance = <Balance<T>>::get(collection_id, &owner.as_sub())2594            .checked_add(value)2595            .unwrap();2596        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2597    }25982599    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2600        // add to account limit2601        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {26022603            // bound Owned tokens by a single address2604            let count = <AccountItemCount<T>>::get(owner.as_sub());2605            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);26062607            <AccountItemCount<T>>::insert(owner.as_sub(), count2608                .checked_add(1)2609                .ok_or(Error::<T>::NumOverflow)?);2610        }2611        else {2612            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2613        }26142615        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2616        if list_exists {2617            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2618            let item_contains = list.contains(&item_index.clone());26192620            if !item_contains {2621                list.push(item_index.clone());2622            }26232624            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2625        } else {2626            let mut itm = Vec::new();2627            itm.push(item_index.clone());2628            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2629        }26302631        Ok(())2632    }26332634    fn remove_token_index(2635        collection_id: CollectionId,2636        item_index: TokenId,2637        owner: &T::CrossAccountId,2638    ) -> DispatchResult {26392640        // update counter2641        <AccountItemCount<T>>::insert(owner.as_sub(), 2642            <AccountItemCount<T>>::get(owner.as_sub())2643            .checked_sub(1)2644            .ok_or(Error::<T>::NumOverflow)?);264526462647        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2648        if list_exists {2649            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2650            let item_contains = list.contains(&item_index.clone());26512652            if item_contains {2653                list.retain(|&item| item != item_index);2654                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2655            }2656        }26572658        Ok(())2659    }26602661    fn move_token_index(2662        collection_id: CollectionId,2663        item_index: TokenId,2664        old_owner: &T::CrossAccountId,2665        new_owner: &T::CrossAccountId,2666    ) -> DispatchResult {2667        Self::remove_token_index(collection_id, item_index, old_owner)?;2668        Self::add_token_index(collection_id, item_index, new_owner)?;26692670        Ok(())2671    }2672    2673    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2674        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26752676        Ok(())2677    }2678}26792680////////////////////////////////////////////////////////////////////////////////////////////////////2681// Economic models2682// #region26832684/// Fee multiplier.2685pub type Multiplier = FixedU128;26862687type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26882689/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2690/// in the queue.2691#[derive(Encode, Decode, Clone, Eq, PartialEq)]2692pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26932694impl<T: Config + Send + Sync> sp_std::fmt::Debug 2695    for ChargeTransactionPayment<T>2696{2697	#[cfg(feature = "std")]2698	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2699		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2700	}2701	#[cfg(not(feature = "std"))]2702	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2703		Ok(())2704	}2705}27062707impl<T: Config> ChargeTransactionPayment<T>2708where2709    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2710    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2711    T::AccountId: AsRef<[u8]>,2712    T::AccountId: UncheckedFrom<T::Hash>,2713{2714    fn traditional_fee(2715        len: usize,2716        info: &DispatchInfoOf<T::Call>,2717        tip: BalanceOf<T>,2718    ) -> BalanceOf<T>2719    where2720        T::Call: Dispatchable<Info = DispatchInfo>,2721    {2722        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2723    }27242725	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2726        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2727        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2728        let len_saturation = max_block_length as u64 / (len as u64).max(1);2729        let coefficient: BalanceOf<T> = weight_saturation2730            .min(len_saturation)2731            .saturated_into::<BalanceOf<T>>();2732        final_fee2733            .saturating_mul(coefficient)2734            .saturated_into::<TransactionPriority>()2735    }27362737    fn withdraw_fee(2738        &self,2739        who: &T::AccountId,2740        call: &T::Call,2741        info: &DispatchInfoOf<T::Call>,2742        len: usize,2743	) -> Result<2744		(2745			BalanceOf<T>,2746			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2747		),2748		TransactionValidityError,2749	> {2750        let tip = self.0;27512752        let fee = Self::traditional_fee(len, info, tip);27532754        // Only mess with balances if fee is not zero.2755        if fee.is_zero() {2756            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2757			.map(|i| (fee, i));2758        }27592760        // Determine who is paying transaction fee based on ecnomic model2761        // Parse call to extract collection ID and access collection sponsor2762        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2763            Some(Call::create_item(collection_id, _owner, _properties)) => {2764                let collection = <CollectionById<T>>::get(collection_id)?;27652766                // sponsor timeout2767                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27682769                let limit = collection.limits.sponsor_transfer_timeout;2770                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2771                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2772                    let limit_time = last_tx_block + limit.into();2773                    if block_number <= limit_time {2774                        return None;2775                    }2776                }2777                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27782779                // check free create limit2780                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2781                    collection.sponsorship.sponsor()2782                        .cloned()2783                } else {2784                    None2785                }2786            }2787            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2788                let collection = <CollectionById<T>>::get(collection_id)?;2789                2790                let mut sponsor_transfer = false;2791                if collection.sponsorship.confirmed() {27922793                    let collection_limits = collection.limits;2794                    let collection_mode = collection.mode;2795    2796                    // sponsor timeout2797                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2798                    sponsor_transfer = match collection_mode {2799                        CollectionMode::NFT => {2800    2801                            // get correct limit2802                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2803                                collection_limits.sponsor_transfer_timeout2804                            } else {2805                                ChainLimit::get().nft_sponsor_transfer_timeout2806                            };2807    2808                            let mut sponsored = true;2809                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2810                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2811                                let limit_time = last_tx_block + limit.into();2812                                if block_number <= limit_time {2813                                    sponsored = false;2814                                }2815                            }2816                            if sponsored {2817                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2818                            }28192820                            sponsored2821                        }2822                        CollectionMode::Fungible(_) => {2823    2824                            // get correct limit2825                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2826                                collection_limits.sponsor_transfer_timeout2827                            } else {2828                                ChainLimit::get().fungible_sponsor_transfer_timeout2829                            };2830    2831                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2832                            let mut sponsored = true;2833                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2834                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2835                                let limit_time = last_tx_block + limit.into();2836                                if block_number <= limit_time {2837                                    sponsored = false;2838                                }2839                            }2840                            if sponsored {2841                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2842                            }28432844                            sponsored2845                        }2846                        CollectionMode::ReFungible => {2847    2848                            // get correct limit2849                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2850                                collection_limits.sponsor_transfer_timeout2851                            } else {2852                                ChainLimit::get().refungible_sponsor_transfer_timeout2853                            };2854    2855                            let mut sponsored = true;2856                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2857                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2858                                let limit_time = last_tx_block + limit.into();2859                                if block_number <= limit_time {2860                                    sponsored = false;2861                                }2862                            }2863                            if sponsored {2864                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2865                            }28662867                            sponsored2868                        }2869                        _ => {2870                            false2871                        },2872                    };2873                }28742875                if !sponsor_transfer {2876                    None2877                } else {2878                    collection.sponsorship.sponsor()2879                        .cloned()2880                }2881            }28822883            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2884                let mut sponsor_metadata_changes = false;28852886                let collection = <CollectionById<T>>::get(collection_id)?;28872888                if2889                    collection.sponsorship.confirmed() &&2890                    // Can't sponsor fungible collection, this tx will be rejected2891                    // as invalid2892                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2893                    data.len() <= collection.limits.sponsored_data_size as usize2894                {2895                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2896                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28972898                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2899                            .map(|last_block| block_number - last_block > rate_limit)2900                            .unwrap_or(true) 2901                        {2902                            sponsor_metadata_changes = true;2903                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2904                        }2905                    }2906                }29072908                if !sponsor_metadata_changes {2909                    None2910                } else {2911                    collection.sponsorship.sponsor().cloned()2912                }2913            }29142915            _ => None,2916        })();29172918        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2919            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29202921                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29222923                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2924                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2925                  2926                if !owned_contract && white_list_enabled {2927                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2928                        return Err(InvalidTransaction::Call.into());2929                    }2930                }2931            },2932            _ => {},2933        }29342935        // Sponsor smart contracts2936        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29372938            // On instantiation: set the contract owner2939            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29402941                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2942                    &who,2943                    code_hash,2944                    salt,2945                );2946                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29472948                None2949            },29502951            // On instantiation with code: set the contract owner2952            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {29532954                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2955                    &who,2956                    &T::Hashing::hash(&_code),2957                    _salt,2958                );29592960                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29612962                None2963            }29642965            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2966            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29672968                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29692970                let mut sponsor_transfer = false;2971                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2972                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2973                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2974                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2975                    let limit_time = last_tx_block + rate_limit;29762977                    if block_number >= limit_time {2978                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2979                        sponsor_transfer = true;2980                    }2981                } else {2982                    sponsor_transfer = false;2983                }2984               2985                if sponsor_transfer {2986                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2987                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2988                            return Some(called_contract);2989                        }2990                    }2991                }29922993                None2994            },29952996            _ => None,2997        });29982999        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());30003001		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)3002			.map(|i| (fee, i))3003    }3004}300530063007impl<T: Config + Send + Sync> SignedExtension3008    for ChargeTransactionPayment<T>3009where3010    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,3011    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,3012    T::AccountId: AsRef<[u8]>,3013    T::AccountId: UncheckedFrom<T::Hash>,3014{3015    const IDENTIFIER: &'static str = "ChargeTransactionPayment";3016    type AccountId = T::AccountId;3017    type Call = T::Call;3018    type AdditionalSigned = ();3019    type Pre = (3020        // tip3021        BalanceOf<T>,3022        // who pays fee3023        Self::AccountId,3024		// imbalance resulting from withdrawing the fee3025		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3026    );3027    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3028        Ok(())3029    }30303031    fn validate(3032        &self,3033        who: &Self::AccountId,3034        call: &Self::Call,3035        info: &DispatchInfoOf<Self::Call>,3036        len: usize,3037    ) -> TransactionValidity {3038		let (fee, _) = self.withdraw_fee(who, call, info, len)?;3039		Ok(ValidTransaction {3040			priority: Self::get_priority(len, info, fee),3041			..Default::default()3042		})3043    }30443045    fn pre_dispatch(3046        self,3047        who: &Self::AccountId,3048        call: &Self::Call,3049        info: &DispatchInfoOf<Self::Call>,3050        len: usize,3051    ) -> Result<Self::Pre, TransactionValidityError> {3052        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3053        Ok((self.0, who.clone(), imbalance))3054    }30553056    fn post_dispatch(3057        pre: Self::Pre,3058        info: &DispatchInfoOf<Self::Call>,3059        post_info: &PostDispatchInfoOf<Self::Call>,3060        len: usize,3061        _result: &DispatchResult,3062    ) -> Result<(), TransactionValidityError> {3063		let (tip, who, imbalance) = pre;3064		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3065			len as u32,3066			info,3067			post_info,3068			tip,3069		);3070		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3071		Ok(())3072    }3073}30743075// #endregion30763077sp_api::decl_runtime_apis! {3078    pub trait NftApi {3079        /// Used for ethereum integration3080        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3081    }3082}