git.delta.rocks / unique-network / refs/commits / 8e8a17552328

difftreelog

source

pallets/nft/src/lib.rs111.7 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)]91011pub use serde::{Serialize, Deserialize};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::*;60use eth::erc::{ERC20Events, ERC721Events};6162pub const MAX_DECIMAL_POINTS: DecimalPoints = 30;63pub const MAX_REFUNGIBLE_PIECES: u128 = 1_000_000_000_000_000_000_000;64pub const MAX_SPONSOR_TIMEOUT: u32 = 10_368_000;65pub const MAX_TOKEN_OWNERSHIP: u32 = 10_000_000;6667// Structs68// #region6970pub type CollectionId = u32;71pub type TokenId = u32;72pub type DecimalPoints = u8;7374#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]75#[derive(Serialize, Deserialize)]76pub enum CollectionMode {77    Invalid,78    NFT,79    // decimal points80    Fungible(DecimalPoints),81    ReFungible,82}8384impl Default for CollectionMode {85    fn default() -> Self {86        Self::Invalid87    }88}8990impl Into<u8> for CollectionMode {91    fn into(self) -> u8 {92        match self {93            CollectionMode::Invalid => 0,94            CollectionMode::NFT => 1,95            CollectionMode::Fungible(_) => 2,96            CollectionMode::ReFungible => 3,97        }98    }99}100101#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AccessMode {104    Normal,105    WhiteList,106}107impl Default for AccessMode {108    fn default() -> Self {109        Self::Normal110    }111}112113#[derive(Encode, Decode, Eq, Debug, Clone, PartialEq)]114#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]115pub enum SchemaVersion {116    ImageURL,117    Unique,118}119impl Default for SchemaVersion {120    fn default() -> Self {121        Self::ImageURL122    }123}124125#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]126#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]127pub struct Ownership<AccountId> {128    pub owner: AccountId,129    pub fraction: u128,130}131132#[derive(Encode, Decode, Debug, Clone, PartialEq)]133#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]134pub enum SponsorshipState<AccountId> {135    /// The fees are applied to the transaction sender136    Disabled,137    Unconfirmed(AccountId),138    /// Transactions are sponsored by specified account139    Confirmed(AccountId),140}141142impl<AccountId> SponsorshipState<AccountId> {143    fn sponsor(&self) -> Option<&AccountId> {144        match self {145            Self::Confirmed(sponsor) => Some(sponsor),146            _ => None,147        }148    }149150    fn pending_sponsor(&self) -> Option<&AccountId> {151        match self {152            Self::Unconfirmed(sponsor) | Self::Confirmed(sponsor) => Some(sponsor),153            _ => None,154        }155    }156157    fn confirmed(&self) -> bool {158        matches!(self, Self::Confirmed(_))159    }160}161162impl<T> Default for SponsorshipState<T> {163    fn default() -> Self {164        Self::Disabled165    }166}167168#[derive(Encode, Decode, Clone, PartialEq)]169#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]170pub struct Collection<T: Config> {171    pub owner: T::AccountId,172    pub mode: CollectionMode,173    pub access: AccessMode,174    pub decimal_points: DecimalPoints,175    pub name: Vec<u16>,        // 64 include null escape char176    pub description: Vec<u16>, // 256 include null escape char177    pub token_prefix: Vec<u8>, // 16 include null escape char178    pub mint_mode: bool,179    pub offchain_schema: Vec<u8>,180    pub schema_version: SchemaVersion,181    pub sponsorship: SponsorshipState<T::AccountId>,182    pub limits: CollectionLimits<T::BlockNumber>, // Collection private restrictions 183    pub variable_on_chain_schema: Vec<u8>, //184    pub const_on_chain_schema: Vec<u8>, //185}186187pub struct CollectionHandle<T: Config> {188    pub id: CollectionId,189    collection: Collection<T>,190    logs: eth::log::LogRecorder,191}192impl<T: Config> CollectionHandle<T> {193	pub fn get(id: CollectionId) -> Option<Self> {194		<CollectionById<T>>::get(id)195			.map(|collection| Self {196				id,197				collection,198                logs: eth::log::LogRecorder::default(),199			})200	}201    pub fn log(&self, log: impl evm_coder::ToLog) {202        self.logs.log(log.to_log(self.evm_address))203    }204    pub fn into_inner(self) -> Collection<T> {205        self.collection.clone()206    }207}208209impl<T: Config> Deref for CollectionHandle<T> {210    type Target = Collection<T>;211212    fn deref(&self) -> &Self::Target {213        &self.collection214    }215}216217impl<T: Config> DerefMut for CollectionHandle<T> {218    fn deref_mut(&mut self) -> &mut Self::Target {219        &mut self.collection220    }221}222223#[derive(Encode, Decode, Debug, Clone, PartialEq)]224#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]225pub struct NftItemType<AccountId> {226    pub owner: AccountId,227    pub const_data: Vec<u8>,228    pub variable_data: Vec<u8>,229}230231#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]232#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]233pub struct FungibleItemType {234    pub value: u128,235}236237#[derive(Encode, Decode, Debug, Clone, PartialEq)]238#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]239pub struct ReFungibleItemType<AccountId> {240    pub owner: Vec<Ownership<AccountId>>,241    pub const_data: Vec<u8>,242    pub variable_data: Vec<u8>,243}244245// #[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]246// #[cfg_attr(feature = "std", derive(Serialize, Deserialize))]247// pub struct VestingItem<AccountId, Moment> {248//     pub sender: AccountId,249//     pub recipient: AccountId,250//     pub collection_id: CollectionId,251//     pub item_id: TokenId,252//     pub amount: u64,253//     pub vesting_date: Moment,254// }255256#[derive(Encode, Decode, Debug, Clone, PartialEq)]257#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]258pub struct CollectionLimits<BlockNumber: Encode + Decode> {259    pub account_token_ownership_limit: u32,260    pub sponsored_data_size: u32,261    /// None - setVariableMetadata is not sponsored262    /// Some(v) - setVariableMetadata is sponsored 263    ///           if there is v block between txs264    pub sponsored_data_rate_limit: Option<BlockNumber>,265    pub token_limit: u32,266267    // Timeouts for item types in passed blocks268    pub sponsor_transfer_timeout: u32,269    pub owner_can_transfer: bool,270    pub owner_can_destroy: bool,271}272273impl<BlockNumber: Encode + Decode> Default for CollectionLimits<BlockNumber> {274    fn default() -> Self {275        Self { 276            account_token_ownership_limit: 10_000_000, 277            token_limit: u32::max_value(),278            sponsored_data_size: u32::MAX, 279            sponsored_data_rate_limit: None,280            sponsor_transfer_timeout: 14400,281            owner_can_transfer: true,282            owner_can_destroy: true283        }284    }285}286287#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]288#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]289pub struct ChainLimits {290    pub collection_numbers_limit: u32,291    pub account_token_ownership_limit: u32,292    pub collections_admins_limit: u64,293    pub custom_data_limit: u32,294295    // Timeouts for item types in passed blocks296    pub nft_sponsor_transfer_timeout: u32,297    pub fungible_sponsor_transfer_timeout: u32,298    pub refungible_sponsor_transfer_timeout: u32,299300    // Schema limits301    pub offchain_schema_limit: u32,302    pub variable_on_chain_schema_limit: u32,303    pub const_on_chain_schema_limit: u32,304}305306pub trait WeightInfo {307	fn create_collection() -> Weight;308	fn destroy_collection() -> Weight;309	fn add_to_white_list() -> Weight;310	fn remove_from_white_list() -> Weight;311    fn set_public_access_mode() -> Weight;312    fn set_mint_permission() -> Weight;313    fn change_collection_owner() -> Weight;314    fn add_collection_admin() -> Weight;315    fn remove_collection_admin() -> Weight;316    fn set_collection_sponsor() -> Weight;317    fn confirm_sponsorship() -> Weight;318    fn remove_collection_sponsor() -> Weight;319    fn create_item(s: usize) -> Weight;320    fn burn_item() -> Weight;321    fn transfer() -> Weight;322    fn approve() -> Weight;323    fn transfer_from() -> Weight;324    fn set_offchain_schema() -> Weight;325    fn set_const_on_chain_schema() -> Weight;326    fn set_variable_on_chain_schema() -> Weight;327    fn set_variable_meta_data() -> Weight;328    fn enable_contract_sponsoring() -> Weight;329    fn set_schema_version() -> Weight;330    fn set_chain_limits() -> Weight;331    fn set_contract_sponsoring_rate_limit() -> Weight;332    fn set_variable_meta_data_sponsoring_rate_limit() -> Weight;333    fn toggle_contract_white_list() -> Weight;334    fn add_to_contract_white_list() -> Weight;335    fn remove_from_contract_white_list() -> Weight;336    fn set_collection_limits() -> Weight;337}338339#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]340#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]341pub struct CreateNftData {342    pub const_data: Vec<u8>,343    pub variable_data: Vec<u8>,344}345346#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]347#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]348pub struct CreateFungibleData {349    pub value: u128,350}351352#[derive(Encode, Decode, Default, Debug, Clone, PartialEq)]353#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]354pub struct CreateReFungibleData {355    pub const_data: Vec<u8>,356    pub variable_data: Vec<u8>,357    pub pieces: u128,358}359360#[derive(Encode, Decode, Debug, Clone, PartialEq)]361#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]362pub enum CreateItemData {363    NFT(CreateNftData),364    Fungible(CreateFungibleData),365    ReFungible(CreateReFungibleData),366}367368impl CreateItemData {369    pub fn len(&self) -> usize {370        let len = match self {371            CreateItemData::NFT(data) => data.variable_data.len() + data.const_data.len(),372            CreateItemData::ReFungible(data) => data.variable_data.len() + data.const_data.len(),373            _ => 0374        };375        376        return len;377    }378}379380impl From<CreateNftData> for CreateItemData {381    fn from(item: CreateNftData) -> Self {382        CreateItemData::NFT(item)383    }384}385386impl From<CreateReFungibleData> for CreateItemData {387    fn from(item: CreateReFungibleData) -> Self {388        CreateItemData::ReFungible(item)389    }390}391392impl From<CreateFungibleData> for CreateItemData {393    fn from(item: CreateFungibleData) -> Self {394        CreateItemData::Fungible(item)395    }396}397398399decl_error! {400	/// Error for non-fungible-token module.401	pub enum Error for Module<T: Config> {402        /// Total collections bound exceeded.403        TotalCollectionsLimitExceeded,404		/// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.405        CollectionDecimalPointLimitExceeded, 406        /// Collection name can not be longer than 63 char.407        CollectionNameLimitExceeded, 408        /// Collection description can not be longer than 255 char.409        CollectionDescriptionLimitExceeded, 410        /// Token prefix can not be longer than 15 char.411        CollectionTokenPrefixLimitExceeded,412        /// This collection does not exist.413        CollectionNotFound,414        /// Item not exists.415        TokenNotFound,416        /// Admin not found417        AdminNotFound,418        /// Arithmetic calculation overflow.419        NumOverflow,       420        /// Account already has admin role.421        AlreadyAdmin,  422        /// You do not own this collection.423        NoPermission,424        /// This address is not set as sponsor, use setCollectionSponsor first.425        ConfirmUnsetSponsorFail,426        /// Collection is not in mint mode.427        PublicMintingNotAllowed,428        /// Sender parameter and item owner must be equal.429        MustBeTokenOwner,430        /// Item balance not enough.431        TokenValueTooLow,432        /// Size of item is too large.433        NftSizeLimitExceeded,434        /// No approve found435        ApproveNotFound,436        /// Requested value more than approved.437        TokenValueNotEnough,438        /// Only approved addresses can call this method.439        ApproveRequired,440        /// Address is not in white list.441        AddresNotInWhiteList,442        /// Number of collection admins bound exceeded.443        CollectionAdminsLimitExceeded,444        /// Owned tokens by a single address bound exceeded.445        AddressOwnershipLimitExceeded,446        /// Length of items properties must be greater than 0.447        EmptyArgument,448        /// const_data exceeded data limit.449        TokenConstDataLimitExceeded,450        /// variable_data exceeded data limit.451        TokenVariableDataLimitExceeded,452        /// Not NFT item data used to mint in NFT collection.453        NotNftDataUsedToMintNftCollectionToken,454        /// Not Fungible item data used to mint in Fungible collection.455        NotFungibleDataUsedToMintFungibleCollectionToken,456        /// Not Re Fungible item data used to mint in Re Fungible collection.457        NotReFungibleDataUsedToMintReFungibleCollectionToken,458        /// Unexpected collection type.459        UnexpectedCollectionType,460        /// Can't store metadata in fungible tokens.461        CantStoreMetadataInFungibleTokens,462        /// Collection token limit exceeded463        CollectionTokenLimitExceeded,464        /// Account token limit exceeded per collection465        AccountTokenLimitExceeded,466        /// Collection limit bounds per collection exceeded467        CollectionLimitBoundsExceeded,468        /// Tried to enable permissions which are only permitted to be disabled469        OwnerPermissionsCantBeReverted,470        /// Schema data size limit bound exceeded471        SchemaDataLimitExceeded,472        /// Maximum refungibility exceeded473        WrongRefungiblePieces,474        /// createRefungible should be called with one owner475        BadCreateRefungibleCall,476	}477}478479pub trait Config: system::Config + Sized + pallet_transaction_payment::Config + pallet_contracts::Config {480    type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;481482    /// Weight information for extrinsics in this pallet.483	type WeightInfo: WeightInfo;484485    type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;486    type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;487    type EvmWithdrawOrigin: pallet_evm::EnsureAddressOrigin<Self::Origin, Success = Self::AccountId>;488489	type CrossAccountId: CrossAccountId<Self::AccountId>;490    type Currency: Currency<Self::AccountId>;491    type CollectionCreationPrice: Get<<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance>;492    type TreasuryAccountId: Get<Self::AccountId>;493494    type EthereumChainId: Get<u64>;495    type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;496}497498#[cfg(feature = "runtime-benchmarks")]499mod benchmarking;500501// #endregion502503// # Used definitions504//505// ## User control levels506//507// chain-controlled - key is uncontrolled by user508//                    i.e autoincrementing index509//                    can use non-cryptographic hash510// real - key is controlled by user511//        but it is hard to generate enough colliding values, i.e owner of signed txs512//        can use non-cryptographic hash513// controlled - key is completly controlled by users514//              i.e maps with mutable keys515//              should use cryptographic hash516//517// ## User control level downgrade reasons518//519// ?1 - chain-controlled -> controlled520//      collections/tokens can be destroyed, resulting in massive holes521// ?2 - chain-controlled -> controlled522//      same as ?1, but can be only added, resulting in easier exploitation523// ?3 - real -> controlled524//      no confirmation required, so addresses can be easily generated525decl_storage! {526    trait Store for Module<T: Config> as Nft {527528        //#region Private members529        /// Id of next collection530        CreatedCollectionCount: u32;531        /// Used for migrations532        ChainVersion: u64;533        /// Id of last collection token534        /// Collection id (controlled?1)535        ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;536        //#endregion537538        //#region Chain limits struct539        pub ChainLimit get(fn chain_limit) config(): ChainLimits;540        //#endregion541542        //#region Bound counters543        /// Amount of collections destroyed, used for total amount tracking with544        /// CreatedCollectionCount545        DestroyedCollectionCount: u32;546        /// Total amount of account owned tokens (NFTs + RFTs + unique fungibles)547        /// Account id (real)548        pub AccountItemCount get(fn account_item_count): map hasher(twox_64_concat) T::AccountId => u32;549        //#endregion550551        //#region Basic collections552        /// Collection info553        /// Collection id (controlled?1)554        pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;555        /// List of collection admins556        /// Collection id (controlled?2)557        pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;558        /// Whitelisted collection users559        /// Collection id (controlled?2), user id (controlled?3)560        pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;561        //#endregion562563        /// How many of collection items user have564        /// Collection id (controlled?2), account id (real)565        pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;566567        /// Amount of items which spender can transfer out of owners account (via transferFrom)568        /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))569        /// TODO: Off chain worker should remove from this map when token gets removed570        pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;571572        //#region Item collections573        /// Collection id (controlled?2), token id (controlled?1)574        pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;575        /// Collection id (controlled?2), owner (controlled?2)576        pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;577        /// Collection id (controlled?2), token id (controlled?1)578        pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;579        //#endregion580581        //#region Index list582        /// Collection id (controlled?2), tokens owner (controlled?2)583        pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;584        //#endregion585586        //#region Tokens transfer rate limit baskets587        /// (Collection id (controlled?2), who created (real))588        /// TODO: Off chain worker should remove from this map when collection gets removed589        pub CreateItemBasket get(fn create_item_basket): map hasher(blake2_128_concat) (CollectionId, T::AccountId) => T::BlockNumber;590        /// Collection id (controlled?2), token id (controlled?2)591        pub NftTransferBasket get(fn nft_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;592        /// Collection id (controlled?2), owning user (real)593        pub FungibleTransferBasket get(fn fungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => T::BlockNumber;594        /// Collection id (controlled?2), token id (controlled?2)595        pub ReFungibleTransferBasket get(fn refungible_transfer_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => T::BlockNumber;596        //#endregion597598        /// Variable metadata sponsoring599        /// Collection id (controlled?2), token id (controlled?2)600        pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;601      602        //#region Contract Sponsorship and Ownership603        /// Contract address (real)604        pub ContractOwner get(fn contract_owner): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>;605        /// Contract address (real)606        pub ContractSelfSponsoring get(fn contract_self_sponsoring): map hasher(twox_64_concat) T::AccountId => bool;607        /// (Contract address(real), caller (real))608        pub ContractSponsorBasket get(fn contract_sponsor_basket): map hasher(twox_64_concat) (T::AccountId, T::AccountId) => T::BlockNumber;609        /// Contract address (real)610        pub ContractSponsoringRateLimit get(fn contract_sponsoring_rate_limit): map hasher(twox_64_concat) T::AccountId => T::BlockNumber;611        /// Contract address (real)612        pub ContractWhiteListEnabled get(fn contract_white_list_enabled): map hasher(twox_64_concat) T::AccountId => bool; 613        /// Contract address (real) => Whitelisted user (controlled?3)614        pub ContractWhiteList get(fn contract_white_list): double_map hasher(twox_64_concat) T::AccountId, hasher(blake2_128_concat) T::AccountId => bool; 615        //#endregion616    }617    add_extra_genesis {618        build(|config: &GenesisConfig<T>| {619            // Modification of storage620            for (_num, _c) in &config.collection_id {621                <Module<T>>::init_collection(_c);622            }623624            for (_num, _c, _i) in &config.nft_item_id {625                <Module<T>>::init_nft_token(*_c, _i);626            }627628            for (collection_id, account_id, fungible_item) in &config.fungible_item_id {629                <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);630            }631632            for (_num, _c, _i) in &config.refungible_item_id {633                <Module<T>>::init_refungible_token(*_c, _i);634            }635        })636    }637}638639decl_event!(640    pub enum Event<T>641    where642        AccountId = <T as frame_system::Config>::AccountId,643        CrossAccountId = <T as Config>::CrossAccountId,644    {645        /// New collection was created646        /// 647        /// # Arguments648        /// 649        /// * collection_id: Globally unique identifier of newly created collection.650        /// 651        /// * mode: [CollectionMode] converted into u8.652        /// 653        /// * account_id: Collection owner.654        CollectionCreated(CollectionId, u8, AccountId),655656        /// New item was created.657        /// 658        /// # Arguments659        /// 660        /// * collection_id: Id of the collection where item was created.661        /// 662        /// * item_id: Id of an item. Unique within the collection.663        ///664        /// * recipient: Owner of newly created item 665        ItemCreated(CollectionId, TokenId, CrossAccountId),666667        /// Collection item was burned.668        /// 669        /// # Arguments670        /// 671        /// collection_id.672        /// 673        /// item_id: Identifier of burned NFT.674        ItemDestroyed(CollectionId, TokenId),675676        /// Item was transferred677        ///678        /// * collection_id: Id of collection to which item is belong679        ///680        /// * item_id: Id of an item681        ///682        /// * sender: Original owner of item683        ///684        /// * recipient: New owner of item685        ///686        /// * amount: Always 1 for NFT687        Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),688689        /// * collection_id690        ///691        /// * item_id692        ///693        /// * sender694        ///695        /// * spender696        ///697        /// * amount698        Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),699    }700);701702decl_module! {703    pub struct Module<T: Config> for enum Call 704    where 705        origin: T::Origin706    {707        fn deposit_event() = default;708        type Error = Error<T>;709710        fn on_initialize(now: T::BlockNumber) -> Weight {711            0712        }713714        /// 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.715        /// 716        /// # Permissions717        /// 718        /// * Anyone.719        /// 720        /// # Arguments721        /// 722        /// * collection_name: UTF-16 string with collection name (limit 64 characters), will be stored as zero-terminated.723        /// 724        /// * collection_description: UTF-16 string with collection description (limit 256 characters), will be stored as zero-terminated.725        /// 726        /// * token_prefix: UTF-8 string with token prefix.727        /// 728        /// * mode: [CollectionMode] collection type and type dependent data.729        // returns collection ID730        #[weight = <T as Config>::WeightInfo::create_collection()]731        #[transactional]732        pub fn create_collection(origin,733                                 collection_name: Vec<u16>,734                                 collection_description: Vec<u16>,735                                 token_prefix: Vec<u8>,736                                 mode: CollectionMode) -> DispatchResult {737738            // Anyone can create a collection739            let who = ensure_signed(origin)?;740741            // Take a (non-refundable) deposit of collection creation742            let mut imbalance = <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();743            imbalance.subsume(<<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(744                &T::TreasuryAccountId::get(),745                T::CollectionCreationPrice::get(),746            ));747            <T as Config>::Currency::settle(748                &who,749                imbalance,750                WithdrawReasons::TRANSFER,751                ExistenceRequirement::KeepAlive,752            ).map_err(|_| Error::<T>::NoPermission)?;753754            let decimal_points = match mode {755                CollectionMode::Fungible(points) => points,756                _ => 0757            };758759            let chain_limit = ChainLimit::get();760761            let created_count = CreatedCollectionCount::get();762            let destroyed_count = DestroyedCollectionCount::get();763764            // bound Total number of collections765            ensure!(created_count - destroyed_count < chain_limit.collection_numbers_limit, Error::<T>::TotalCollectionsLimitExceeded);766767            // check params768            ensure!(decimal_points <= MAX_DECIMAL_POINTS, Error::<T>::CollectionDecimalPointLimitExceeded);769            ensure!(collection_name.len() <= 64, Error::<T>::CollectionNameLimitExceeded);770            ensure!(collection_description.len() <= 256, Error::<T>::CollectionDescriptionLimitExceeded);771            ensure!(token_prefix.len() <= 16, Error::<T>::CollectionTokenPrefixLimitExceeded);772773            // Generate next collection ID774            let next_id = created_count775                .checked_add(1)776                .ok_or(Error::<T>::NumOverflow)?;777778            CreatedCollectionCount::put(next_id);779780            let limits = CollectionLimits {781                sponsored_data_size: chain_limit.custom_data_limit,782                ..Default::default()783            };784785            // Create new collection786            let new_collection = Collection {787                owner: who.clone(),788                name: collection_name,789                mode: mode.clone(),790                mint_mode: false,791                access: AccessMode::Normal,792                description: collection_description,793                decimal_points: decimal_points,794                token_prefix: token_prefix,795                offchain_schema: Vec::new(),796                schema_version: SchemaVersion::ImageURL,797                sponsorship: SponsorshipState::Disabled,798                variable_on_chain_schema: Vec::new(),799                const_on_chain_schema: Vec::new(),800                limits,801            };802803            // Add new collection to map804            <CollectionById<T>>::insert(next_id, new_collection);805806            // call event807            Self::deposit_event(RawEvent::CollectionCreated(next_id, mode.into(), who));808809            Ok(())810        }811812        /// **DANGEROUS**: Destroys collection and all NFTs within this collection. Users irrecoverably lose their assets and may lose real money.813        /// 814        /// # Permissions815        /// 816        /// * Collection Owner.817        /// 818        /// # Arguments819        /// 820        /// * collection_id: collection to destroy.821        #[weight = <T as Config>::WeightInfo::destroy_collection()]822        #[transactional]823        pub fn destroy_collection(origin, collection_id: CollectionId) -> DispatchResult {824825            let sender = ensure_signed(origin)?;826            let collection = Self::get_collection(collection_id)?;827            Self::check_owner_permissions(&collection, &sender)?;828            if !collection.limits.owner_can_destroy {829                fail!(Error::<T>::NoPermission);830            }831832            <AddressTokens<T>>::remove_prefix(collection_id);833            <Allowances<T>>::remove_prefix(collection_id);834            <Balance<T>>::remove_prefix(collection_id);835            <ItemListIndex>::remove(collection_id);836            <AdminList<T>>::remove(collection_id);837            <CollectionById<T>>::remove(collection_id);838            <WhiteList<T>>::remove_prefix(collection_id);839840            <NftItemList<T>>::remove_prefix(collection_id);841            <FungibleItemList<T>>::remove_prefix(collection_id);842            <ReFungibleItemList<T>>::remove_prefix(collection_id);843844            <NftTransferBasket<T>>::remove_prefix(collection_id);845            <FungibleTransferBasket<T>>::remove_prefix(collection_id);846            <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);847848            <VariableMetaDataBasket<T>>::remove_prefix(collection_id);849850            DestroyedCollectionCount::put(DestroyedCollectionCount::get()851                .checked_add(1)852                .ok_or(Error::<T>::NumOverflow)?);853854            Ok(())855        }856857        /// Add an address to white list.858        /// 859        /// # Permissions860        /// 861        /// * Collection Owner862        /// * Collection Admin863        /// 864        /// # Arguments865        /// 866        /// * collection_id.867        /// 868        /// * address.869        #[weight = <T as Config>::WeightInfo::add_to_white_list()]870        #[transactional]871        pub fn add_to_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{872873            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);874            let collection = Self::get_collection(collection_id)?;875876            Self::toggle_white_list_internal(877                &sender,878                &collection,879                &address,880                true,881            )?;882883            Ok(())884        }885886        /// Remove an address from white list.887        /// 888        /// # Permissions889        /// 890        /// * Collection Owner891        /// * Collection Admin892        /// 893        /// # Arguments894        /// 895        /// * collection_id.896        /// 897        /// * address.898        #[weight = <T as Config>::WeightInfo::remove_from_white_list()]899        #[transactional]900        pub fn remove_from_white_list(origin, collection_id: CollectionId, address: T::CrossAccountId) -> DispatchResult{901902            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);903            let collection = Self::get_collection(collection_id)?;904905            Self::toggle_white_list_internal(906                &sender,907                &collection,908                &address,909                false,910            )?;911912            Ok(())913        }914915        /// Toggle between normal and white list access for the methods with access for `Anyone`.916        /// 917        /// # Permissions918        /// 919        /// * Collection Owner.920        /// 921        /// # Arguments922        /// 923        /// * collection_id.924        /// 925        /// * mode: [AccessMode]926        #[weight = <T as Config>::WeightInfo::set_public_access_mode()]927        #[transactional]928        pub fn set_public_access_mode(origin, collection_id: CollectionId, mode: AccessMode) -> DispatchResult929        {930            let sender = ensure_signed(origin)?;931932            let mut target_collection = Self::get_collection(collection_id)?;933            Self::check_owner_permissions(&target_collection, &sender)?;934            target_collection.access = mode;935            Self::save_collection(target_collection);936937            Ok(())938        }939940        /// Allows Anyone to create tokens if:941        /// * White List is enabled, and942        /// * Address is added to white list, and943        /// * This method was called with True parameter944        /// 945        /// # Permissions946        /// * Collection Owner947        ///948        /// # Arguments949        /// 950        /// * collection_id.951        /// 952        /// * mint_permission: Boolean parameter. If True, allows minting to Anyone with conditions above.953        #[weight = <T as Config>::WeightInfo::set_mint_permission()]954        #[transactional]955        pub fn set_mint_permission(origin, collection_id: CollectionId, mint_permission: bool) -> DispatchResult956        {957            let sender = ensure_signed(origin)?;958959            let mut target_collection = Self::get_collection(collection_id)?;960            Self::check_owner_permissions(&target_collection, &sender)?;961            target_collection.mint_mode = mint_permission;962            Self::save_collection(target_collection);963964            Ok(())965        }966967        /// Change the owner of the collection.968        /// 969        /// # Permissions970        /// 971        /// * Collection Owner.972        /// 973        /// # Arguments974        /// 975        /// * collection_id.976        /// 977        /// * new_owner.978        #[weight = <T as Config>::WeightInfo::change_collection_owner()]979        #[transactional]980        pub fn change_collection_owner(origin, collection_id: CollectionId, new_owner: T::AccountId) -> DispatchResult {981982            let sender = ensure_signed(origin)?;983            let mut target_collection = Self::get_collection(collection_id)?;984            Self::check_owner_permissions(&target_collection, &sender)?;985            target_collection.owner = new_owner;986            Self::save_collection(target_collection);987988            Ok(())989        }990991        /// Adds an admin of the Collection.992        /// 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. 993        /// 994        /// # Permissions995        /// 996        /// * Collection Owner.997        /// * Collection Admin.998        /// 999        /// # Arguments1000        /// 1001        /// * collection_id: ID of the Collection to add admin for.1002        /// 1003        /// * new_admin_id: Address of new admin to add.1004        #[weight = <T as Config>::WeightInfo::add_collection_admin()]1005        #[transactional]1006        pub fn add_collection_admin(origin, collection_id: CollectionId, new_admin_id: T::CrossAccountId) -> DispatchResult {1007            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1008            let collection = Self::get_collection(collection_id)?;1009            Self::check_owner_or_admin_permissions(&collection, &sender)?;1010            let mut admin_arr = <AdminList<T>>::get(collection_id);10111012            match admin_arr.binary_search(&new_admin_id) {1013                Ok(_) => {},1014                Err(idx) => {1015                    let limits = ChainLimit::get();1016                    ensure!(admin_arr.len() < limits.collections_admins_limit as usize, Error::<T>::CollectionAdminsLimitExceeded);1017                    admin_arr.insert(idx, new_admin_id);1018                    <AdminList<T>>::insert(collection_id, admin_arr);1019                }1020            }1021            Ok(())1022        }10231024        /// 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.1025        ///1026        /// # Permissions1027        /// 1028        /// * Collection Owner.1029        /// * Collection Admin.1030        /// 1031        /// # Arguments1032        /// 1033        /// * collection_id: ID of the Collection to remove admin for.1034        /// 1035        /// * account_id: Address of admin to remove.1036        #[weight = <T as Config>::WeightInfo::remove_collection_admin()]1037        #[transactional]1038        pub fn remove_collection_admin(origin, collection_id: CollectionId, account_id: T::CrossAccountId) -> DispatchResult {1039            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1040            let collection = Self::get_collection(collection_id)?;1041            Self::check_owner_or_admin_permissions(&collection, &sender)?;1042            let mut admin_arr = <AdminList<T>>::get(collection_id);10431044            match admin_arr.binary_search(&account_id) {1045                Ok(idx) => {1046                    admin_arr.remove(idx);1047                    <AdminList<T>>::insert(collection_id, admin_arr);1048                },1049                Err(_) => {}1050            }1051            Ok(())1052        }10531054        /// # Permissions1055        /// 1056        /// * Collection Owner1057        /// 1058        /// # Arguments1059        /// 1060        /// * collection_id.1061        /// 1062        /// * new_sponsor.1063        #[weight = <T as Config>::WeightInfo::set_collection_sponsor()]1064        #[transactional]1065        pub fn set_collection_sponsor(origin, collection_id: CollectionId, new_sponsor: T::AccountId) -> DispatchResult {1066            let sender = ensure_signed(origin)?;1067            let mut target_collection = Self::get_collection(collection_id)?;1068            Self::check_owner_permissions(&target_collection, &sender)?;10691070            target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);1071            Self::save_collection(target_collection);10721073            Ok(())1074        }10751076        /// # Permissions1077        /// 1078        /// * Sponsor.1079        /// 1080        /// # Arguments1081        /// 1082        /// * collection_id.1083        #[weight = <T as Config>::WeightInfo::confirm_sponsorship()]1084        #[transactional]1085        pub fn confirm_sponsorship(origin, collection_id: CollectionId) -> DispatchResult {1086            let sender = ensure_signed(origin)?;10871088            let mut target_collection = Self::get_collection(collection_id)?;1089            ensure!(1090                target_collection.sponsorship.pending_sponsor() == Some(&sender),1091                Error::<T>::ConfirmUnsetSponsorFail1092            );10931094            target_collection.sponsorship = SponsorshipState::Confirmed(sender);1095            Self::save_collection(target_collection);10961097            Ok(())1098        }10991100        /// Switch back to pay-per-own-transaction model.1101        ///1102        /// # Permissions1103        ///1104        /// * Collection owner.1105        /// 1106        /// # Arguments1107        /// 1108        /// * collection_id.1109        #[weight = <T as Config>::WeightInfo::remove_collection_sponsor()]1110        #[transactional]1111        pub fn remove_collection_sponsor(origin, collection_id: CollectionId) -> DispatchResult {1112            let sender = ensure_signed(origin)?;11131114            let mut target_collection = Self::get_collection(collection_id)?;1115            Self::check_owner_permissions(&target_collection, &sender)?;11161117            target_collection.sponsorship = SponsorshipState::Disabled;1118            Self::save_collection(target_collection);11191120            Ok(())1121        }11221123        /// This method creates a concrete instance of NFT Collection created with CreateCollection method.1124        /// 1125        /// # Permissions1126        /// 1127        /// * Collection Owner.1128        /// * Collection Admin.1129        /// * Anyone if1130        ///     * White List is enabled, and1131        ///     * Address is added to white list, and1132        ///     * MintPermission is enabled (see SetMintPermission method)1133        /// 1134        /// # Arguments1135        /// 1136        /// * collection_id: ID of the collection.1137        /// 1138        /// * owner: Address, initial owner of the NFT.1139        ///1140        /// * data: Token data to store on chain.1141        // #[weight =1142        // (130_000_000 as Weight)1143        // .saturating_add((2135 as Weight).saturating_mul((properties.len() as u64) as Weight))1144        // .saturating_add(RocksDbWeight::get().reads(10 as Weight))1145        // .saturating_add(RocksDbWeight::get().writes(8 as Weight))]11461147        #[weight = <T as Config>::WeightInfo::create_item(data.len())]1148        #[transactional]1149        pub fn create_item(origin, collection_id: CollectionId, owner: T::CrossAccountId, data: CreateItemData) -> DispatchResult {1150            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1151            let collection = Self::get_collection(collection_id)?;11521153            Self::create_item_internal(&sender, &collection, &owner, data)?;11541155            Self::submit_logs(collection)?;1156            Ok(())1157        }11581159        /// This method creates multiple items in a collection created with CreateCollection method.1160        /// 1161        /// # Permissions1162        /// 1163        /// * Collection Owner.1164        /// * Collection Admin.1165        /// * Anyone if1166        ///     * White List is enabled, and1167        ///     * Address is added to white list, and1168        ///     * MintPermission is enabled (see SetMintPermission method)1169        /// 1170        /// # Arguments1171        /// 1172        /// * collection_id: ID of the collection.1173        /// 1174        /// * itemsData: Array items properties. Each property is an array of bytes itself, see [create_item].1175        /// 1176        /// * owner: Address, initial owner of the NFT.1177        #[weight = <T as Config>::WeightInfo::create_item(items_data.into_iter()1178                               .map(|data| { data.len() })1179                               .sum())]1180        #[transactional]1181        pub fn create_multiple_items(origin, collection_id: CollectionId, owner: T::CrossAccountId, items_data: Vec<CreateItemData>) -> DispatchResult {11821183            ensure!(items_data.len() > 0, Error::<T>::EmptyArgument);1184            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1185            let collection = Self::get_collection(collection_id)?;11861187            Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;11881189            Self::submit_logs(collection)?;1190            Ok(())1191        }11921193        /// Destroys a concrete instance of NFT.1194        /// 1195        /// # Permissions1196        /// 1197        /// * Collection Owner.1198        /// * Collection Admin.1199        /// * Current NFT Owner.1200        /// 1201        /// # Arguments1202        /// 1203        /// * collection_id: ID of the collection.1204        /// 1205        /// * item_id: ID of NFT to burn.1206        #[weight = <T as Config>::WeightInfo::burn_item()]1207        #[transactional]1208        pub fn burn_item(origin, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {12091210            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1211            let target_collection = Self::get_collection(collection_id)?;12121213            Self::burn_item_internal(&sender, &target_collection, item_id, value)?;12141215            Self::submit_logs(target_collection)?;1216            Ok(())1217        }12181219        /// Change ownership of the token.1220        /// 1221        /// # Permissions1222        /// 1223        /// * Collection Owner1224        /// * Collection Admin1225        /// * Current NFT owner1226        ///1227        /// # Arguments1228        /// 1229        /// * recipient: Address of token recipient.1230        /// 1231        /// * collection_id.1232        /// 1233        /// * item_id: ID of the item1234        ///     * Non-Fungible Mode: Required.1235        ///     * Fungible Mode: Ignored.1236        ///     * Re-Fungible Mode: Required.1237        /// 1238        /// * value: Amount to transfer.1239        ///     * Non-Fungible Mode: Ignored1240        ///     * Fungible Mode: Must specify transferred amount1241        ///     * Re-Fungible Mode: Must specify transferred portion (between 0 and 1)1242        #[weight = <T as Config>::WeightInfo::transfer()]1243        #[transactional]1244        pub fn transfer(origin, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128) -> DispatchResult {1245            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1246            let collection = Self::get_collection(collection_id)?;12471248            Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;12491250            Self::submit_logs(collection)?;1251            Ok(())1252        }12531254        /// Set, change, or remove approved address to transfer the ownership of the NFT.1255        /// 1256        /// # Permissions1257        /// 1258        /// * Collection Owner1259        /// * Collection Admin1260        /// * Current NFT owner1261        /// 1262        /// # Arguments1263        /// 1264        /// * approved: Address that is approved to transfer this NFT or zero (if needed to remove approval).1265        /// 1266        /// * collection_id.1267        /// 1268        /// * item_id: ID of the item.1269        #[weight = <T as Config>::WeightInfo::approve()]1270        #[transactional]1271        pub fn approve(origin, spender: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, amount: u128) -> DispatchResult {1272            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1273            let collection = Self::get_collection(collection_id)?;12741275            Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;12761277            Self::submit_logs(collection)?;1278            Ok(())1279        }1280        1281        /// 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.1282        /// 1283        /// # Permissions1284        /// * Collection Owner1285        /// * Collection Admin1286        /// * Current NFT owner1287        /// * Address approved by current NFT owner1288        /// 1289        /// # Arguments1290        /// 1291        /// * from: Address that owns token.1292        /// 1293        /// * recipient: Address of token recipient.1294        /// 1295        /// * collection_id.1296        /// 1297        /// * item_id: ID of the item.1298        /// 1299        /// * value: Amount to transfer.1300        #[weight = <T as Config>::WeightInfo::transfer_from()]1301        #[transactional]1302        pub fn transfer_from(origin, from: T::CrossAccountId, recipient: T::CrossAccountId, collection_id: CollectionId, item_id: TokenId, value: u128 ) -> DispatchResult {1303            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1304            let collection = Self::get_collection(collection_id)?;13051306            Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;13071308            Self::submit_logs(collection)?;1309            Ok(())1310        }1311        // #[weight = 0]1312        //     // let no_perm_mes = "You do not have permissions to modify this collection";1313        //     // ensure!(<ApprovedList<T>>::contains_key((collection_id, item_id)), no_perm_mes);1314        //     // let list_itm = <ApprovedList<T>>::get((collection_id, item_id));1315        //     // ensure!(list_itm.contains(&new_owner.clone()), no_perm_mes);13161317        //     // // on_nft_received  call13181319        //     // Self::transfer(origin, collection_id, item_id, new_owner)?;13201321        //     Ok(())1322        // }13231324        /// Set off-chain data schema.1325        /// 1326        /// # Permissions1327        /// 1328        /// * Collection Owner1329        /// * Collection Admin1330        /// 1331        /// # Arguments1332        /// 1333        /// * collection_id.1334        /// 1335        /// * schema: String representing the offchain data schema.1336        #[weight = <T as Config>::WeightInfo::set_variable_meta_data()]1337        #[transactional]1338        pub fn set_variable_meta_data (1339            origin,1340            collection_id: CollectionId,1341            item_id: TokenId,1342            data: Vec<u8>1343        ) -> DispatchResult {1344            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1345            1346            let collection = Self::get_collection(collection_id)?;13471348            Self::set_variable_meta_data_internal(&sender, &collection, item_id, data)?;13491350            Ok(())1351        }1352 1353        /// Set schema standard1354        /// ImageURL1355        /// Unique1356        /// 1357        /// # Permissions1358        /// 1359        /// * Collection Owner1360        /// * Collection Admin1361        /// 1362        /// # Arguments1363        /// 1364        /// * collection_id.1365        /// 1366        /// * schema: SchemaVersion: enum1367        #[weight = <T as Config>::WeightInfo::set_schema_version()]1368        #[transactional]1369        pub fn set_schema_version(1370            origin,1371            collection_id: CollectionId,1372            version: SchemaVersion1373        ) -> DispatchResult {1374            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1375            let mut target_collection = Self::get_collection(collection_id)?;1376            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1377            target_collection.schema_version = version;1378            Self::save_collection(target_collection);13791380            Ok(())1381        }13821383        /// Set off-chain data schema.1384        /// 1385        /// # Permissions1386        /// 1387        /// * Collection Owner1388        /// * Collection Admin1389        /// 1390        /// # Arguments1391        /// 1392        /// * collection_id.1393        /// 1394        /// * schema: String representing the offchain data schema.1395        #[weight = <T as Config>::WeightInfo::set_offchain_schema()]1396        #[transactional]1397        pub fn set_offchain_schema(1398            origin,1399            collection_id: CollectionId,1400            schema: Vec<u8>1401        ) -> DispatchResult {1402            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1403            let mut target_collection = Self::get_collection(collection_id)?;1404            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14051406            // check schema limit1407            ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");14081409            target_collection.offchain_schema = schema;1410            Self::save_collection(target_collection);14111412            Ok(())1413        }14141415        /// Set const on-chain data schema.1416        /// 1417        /// # Permissions1418        /// 1419        /// * Collection Owner1420        /// * Collection Admin1421        /// 1422        /// # Arguments1423        /// 1424        /// * collection_id.1425        /// 1426        /// * schema: String representing the const on-chain data schema.1427        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1428        #[transactional]1429        pub fn set_const_on_chain_schema (1430            origin,1431            collection_id: CollectionId,1432            schema: Vec<u8>1433        ) -> DispatchResult {1434            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1435            let mut target_collection = Self::get_collection(collection_id)?;1436            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14371438            // check schema limit1439            ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");14401441            target_collection.const_on_chain_schema = schema;1442            Self::save_collection(target_collection);14431444            Ok(())1445        }14461447        /// Set variable on-chain data schema.1448        /// 1449        /// # Permissions1450        /// 1451        /// * Collection Owner1452        /// * Collection Admin1453        /// 1454        /// # Arguments1455        /// 1456        /// * collection_id.1457        /// 1458        /// * schema: String representing the variable on-chain data schema.1459        #[weight = <T as Config>::WeightInfo::set_const_on_chain_schema()]1460        #[transactional]1461        pub fn set_variable_on_chain_schema (1462            origin,1463            collection_id: CollectionId,1464            schema: Vec<u8>1465        ) -> DispatchResult {1466            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1467            let mut target_collection = Self::get_collection(collection_id)?;1468            Self::check_owner_or_admin_permissions(&target_collection, &sender)?;14691470            // check schema limit1471            ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");14721473            target_collection.variable_on_chain_schema = schema;1474            Self::save_collection(target_collection);14751476            Ok(())1477        }14781479        // Sudo permissions function1480        #[weight = <T as Config>::WeightInfo::set_chain_limits()]1481        #[transactional]1482        pub fn set_chain_limits(1483            origin,1484            limits: ChainLimits1485        ) -> DispatchResult {14861487            #[cfg(not(feature = "runtime-benchmarks"))]1488            ensure_root(origin)?;14891490            <ChainLimit>::put(limits);1491            Ok(())1492        }14931494        /// Enable smart contract self-sponsoring.1495        /// 1496        /// # Permissions1497        /// 1498        /// * Contract Owner1499        /// 1500        /// # Arguments1501        /// 1502        /// * contract address1503        /// * enable flag1504        /// 1505        #[weight = <T as Config>::WeightInfo::enable_contract_sponsoring()]1506        #[transactional]1507        pub fn enable_contract_sponsoring(1508            origin,1509            contract_address: T::AccountId,1510            enable: bool1511        ) -> DispatchResult {15121513            let sender = ensure_signed(origin)?;15141515            #[cfg(feature = "runtime-benchmarks")]1516            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15171518            Self::ensure_contract_owned(sender, &contract_address)?;15191520            <ContractSelfSponsoring<T>>::insert(contract_address, enable);1521            Ok(())1522        }15231524        /// Set the rate limit for contract sponsoring to specified number of blocks.1525        /// 1526        /// If not set (has the default value of 0 blocks), the sponsoring will be disabled. 1527        /// If set to the number B (for blocks), the transactions will be sponsored with a rate 1528        /// limit of B, i.e. fees for every transaction sent to this smart contract will be paid 1529        /// from contract endowment if there are at least B blocks between such transactions. 1530        /// Nonetheless, if transactions are sent more frequently, the fees are paid by the sender.1531        /// 1532        /// # Permissions1533        /// 1534        /// * Contract Owner1535        /// 1536        /// # Arguments1537        /// 1538        /// -`contract_address`: Address of the contract to sponsor1539        /// -`rate_limit`: Number of blocks to wait until the next sponsored transaction is allowed1540        /// 1541        #[weight = <T as Config>::WeightInfo::set_contract_sponsoring_rate_limit()]1542        #[transactional]1543        pub fn set_contract_sponsoring_rate_limit(1544            origin,1545            contract_address: T::AccountId,1546            rate_limit: T::BlockNumber1547        ) -> DispatchResult {1548            let sender = ensure_signed(origin)?;15491550            #[cfg(feature = "runtime-benchmarks")]1551            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15521553            Self::ensure_contract_owned(sender, &contract_address)?;1554            <ContractSponsoringRateLimit<T>>::insert(contract_address, rate_limit);1555            Ok(())1556        }15571558        /// Enable the white list for a contract. Only addresses added to the white list with addToContractWhiteList will be able to call this smart contract.1559        /// 1560        /// # Permissions1561        /// 1562        /// * Address that deployed smart contract.1563        /// 1564        /// # Arguments1565        /// 1566        /// -`contract_address`: Address of the contract.1567        /// 1568        /// - `enable`: .  1569        #[weight = <T as Config>::WeightInfo::toggle_contract_white_list()]1570        #[transactional]1571        pub fn toggle_contract_white_list(1572            origin,1573            contract_address: T::AccountId,1574            enable: bool1575        ) -> DispatchResult {1576            let sender = ensure_signed(origin)?;15771578            #[cfg(feature = "runtime-benchmarks")]1579            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());15801581            Self::ensure_contract_owned(sender, &contract_address)?;1582            if enable {1583                <ContractWhiteListEnabled<T>>::insert(contract_address, true);1584            } else {1585                <ContractWhiteListEnabled<T>>::remove(contract_address);1586            }1587            Ok(())1588        }1589        1590        /// Add an address to smart contract white list.1591        /// 1592        /// # Permissions1593        /// 1594        /// * Address that deployed smart contract.1595        /// 1596        /// # Arguments1597        /// 1598        /// -`contract_address`: Address of the contract.1599        ///1600        /// -`account_address`: Address to add.1601        #[weight = <T as Config>::WeightInfo::add_to_contract_white_list()]1602        #[transactional]1603        pub fn add_to_contract_white_list(1604            origin,1605            contract_address: T::AccountId,1606            account_address: T::AccountId1607        ) -> DispatchResult {1608            let sender = ensure_signed(origin)?;16091610            #[cfg(feature = "runtime-benchmarks")]1611            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());1612            1613            Self::ensure_contract_owned(sender, &contract_address)?;      1614            <ContractWhiteList<T>>::insert(contract_address, account_address, true);1615            Ok(())1616        }16171618        /// Remove an address from smart contract white list.1619        /// 1620        /// # Permissions1621        /// 1622        /// * Address that deployed smart contract.1623        /// 1624        /// # Arguments1625        /// 1626        /// -`contract_address`: Address of the contract.1627        ///1628        /// -`account_address`: Address to remove.1629        #[weight = <T as Config>::WeightInfo::remove_from_contract_white_list()]1630        #[transactional]1631        pub fn remove_from_contract_white_list(1632            origin,1633            contract_address: T::AccountId,1634            account_address: T::AccountId1635        ) -> DispatchResult {1636            let sender = ensure_signed(origin)?;16371638            #[cfg(feature = "runtime-benchmarks")]1639            <ContractOwner<T>>::insert(contract_address.clone(), sender.clone());16401641            Self::ensure_contract_owned(sender, &contract_address)?;1642            <ContractWhiteList<T>>::remove(contract_address, account_address);1643            Ok(())1644        }16451646        #[weight = <T as Config>::WeightInfo::set_collection_limits()]1647        #[transactional]1648        pub fn set_collection_limits(1649            origin,1650            collection_id: u32,1651            new_limits: CollectionLimits<T::BlockNumber>,1652        ) -> DispatchResult {1653            let sender = T::CrossAccountId::from_sub(ensure_signed(origin)?);1654            let mut target_collection = Self::get_collection(collection_id)?;1655            Self::check_owner_permissions(&target_collection, &sender.as_sub())?;1656            let old_limits = &target_collection.limits;1657            let chain_limits = ChainLimit::get();16581659            // collection bounds1660            ensure!(new_limits.sponsor_transfer_timeout <= MAX_SPONSOR_TIMEOUT &&1661                new_limits.account_token_ownership_limit <= MAX_TOKEN_OWNERSHIP && 1662                new_limits.sponsored_data_size <= chain_limits.custom_data_limit,1663                Error::<T>::CollectionLimitBoundsExceeded);16641665            // token_limit   check  prev1666            ensure!(old_limits.token_limit >= new_limits.token_limit, Error::<T>::CollectionTokenLimitExceeded);1667            ensure!(new_limits.token_limit > 0, Error::<T>::CollectionTokenLimitExceeded);16681669            ensure!(1670                (old_limits.owner_can_transfer || !new_limits.owner_can_transfer) &&1671                (old_limits.owner_can_destroy || !new_limits.owner_can_destroy),1672                Error::<T>::OwnerPermissionsCantBeReverted,1673            );16741675            target_collection.limits = new_limits;1676            Self::save_collection(target_collection);16771678            Ok(())1679        } 1680    }1681}16821683impl<T: Config> Module<T> {1684    pub fn create_item_internal(sender: &T::CrossAccountId, collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {1685        Self::can_create_items_in_collection(&collection, &sender, &owner, 1)?;1686        Self::validate_create_item_args(&collection, &data)?;1687        Self::create_item_no_validation(&collection, owner, data)?;16881689        Ok(())1690    }16911692    pub fn transfer_internal(sender: &T::CrossAccountId, recipient: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId, value: u128) -> DispatchResult {1693        // Limits check1694        Self::is_correct_transfer(target_collection, &recipient)?;16951696        // Transfer permissions check1697        ensure!(Self::is_item_owner(&sender, target_collection, item_id) ||1698            Self::is_owner_or_admin_permissions(target_collection, &sender),1699            Error::<T>::NoPermission);17001701        if target_collection.access == AccessMode::WhiteList {1702            Self::check_white_list(target_collection, &sender)?;1703            Self::check_white_list(target_collection, &recipient)?;1704        }17051706        match target_collection.mode1707        {1708            CollectionMode::NFT => Self::transfer_nft(target_collection, item_id, sender.clone(), recipient.clone())?,1709            CollectionMode::Fungible(_)  => Self::transfer_fungible(target_collection, value, &sender, &recipient)?,1710            CollectionMode::ReFungible  => Self::transfer_refungible(target_collection, item_id, value, sender.clone(), recipient.clone())?,1711            _ => ()1712        };17131714        Self::deposit_event(RawEvent::Transfer(target_collection.id, item_id, sender.clone(), recipient.clone(), value));17151716        Ok(())1717    }17181719	pub fn approve_internal(1720		sender: &T::CrossAccountId,1721		spender: &T::CrossAccountId,1722		collection: &CollectionHandle<T>,1723		item_id: TokenId,1724		amount: u1281725	) -> DispatchResult {1726		Self::token_exists(&collection, item_id)?;17271728		// Transfer permissions check1729		let bypasses_limits = collection.limits.owner_can_transfer &&1730			Self::is_owner_or_admin_permissions(1731				&collection,1732				&sender,1733			);17341735		let allowance_limit = if bypasses_limits {1736			None1737		} else if let Some(amount) = Self::owned_amount(1738			&sender,1739			&collection,1740			item_id,1741		) {1742			Some(amount)1743		} else {1744			fail!(Error::<T>::NoPermission);1745		};17461747		if collection.access == AccessMode::WhiteList {1748			Self::check_white_list(&collection, &sender)?;1749			Self::check_white_list(&collection, &spender)?;1750		}17511752		let allowance: u128 = amount1753			.checked_add(<Allowances<T>>::get(collection.id, (item_id, sender.as_sub(), spender.as_sub())))1754			.ok_or(Error::<T>::NumOverflow)?;1755		if let Some(limit) = allowance_limit {1756			ensure!(limit >= allowance, Error::<T>::TokenValueTooLow);1757		}1758		<Allowances<T>>::insert(collection.id, (item_id, sender.as_sub(), spender.as_sub()), allowance);17591760		if matches!(collection.mode, CollectionMode::NFT) {1761			// TODO: NFT: only one owner may exist for token in ERC7211762			collection.log(ERC721Events::Approval {1763                owner: *sender.as_eth(),1764                approved: *spender.as_eth(),1765                token_id: item_id.into(),1766            });1767		}17681769		if matches!(collection.mode, CollectionMode::Fungible(_)) {1770			// TODO: NFT: only one owner may exist for token in ERC201771			collection.log(ERC20Events::Approval {1772                owner: *sender.as_eth(),1773                spender: *spender.as_eth(),1774                value: allowance.into()1775            });1776		}17771778		Self::deposit_event(RawEvent::Approved(collection.id, item_id, sender.clone(), spender.clone(), allowance));1779		Ok(())1780	}17811782	pub fn transfer_from_internal(1783		sender: &T::CrossAccountId,1784		from: &T::CrossAccountId,1785		recipient: &T::CrossAccountId,1786		collection: &CollectionHandle<T>,1787		item_id: TokenId,1788		amount: u128,1789	) -> DispatchResult {1790		// Check approval1791		let approval: u128 = <Allowances<T>>::get(collection.id, (item_id, from.as_sub(), sender.as_sub()));17921793		// Limits check1794		Self::is_correct_transfer(&collection, &recipient)?;17951796		// Transfer permissions check1797		ensure!(1798			approval >= amount || 1799			(1800				collection.limits.owner_can_transfer &&1801				Self::is_owner_or_admin_permissions(&collection, &sender)1802			),1803			Error::<T>::NoPermission1804		);18051806		if collection.access == AccessMode::WhiteList {1807			Self::check_white_list(&collection, &sender)?;1808			Self::check_white_list(&collection, &recipient)?;1809		}18101811		// Reduce approval by transferred amount or remove if remaining approval drops to 01812		let allowance = approval.saturating_sub(amount);1813		if allowance > 0 {1814			<Allowances<T>>::insert(collection.id, (item_id, from.as_sub(), sender.as_sub()), allowance);1815		} else {1816			<Allowances<T>>::remove(collection.id, (item_id, from.as_sub(), sender.as_sub()));1817		}18181819		match collection.mode {1820			CollectionMode::NFT => {1821				Self::transfer_nft(&collection, item_id, from.clone(), recipient.clone())?1822			}1823			CollectionMode::Fungible(_) => {1824				Self::transfer_fungible(&collection, amount, &from, &recipient)?1825			}1826			CollectionMode::ReFungible => {1827				Self::transfer_refungible(&collection, item_id, amount, from.clone(), recipient.clone())?1828			}1829			_ => ()1830		};18311832		if matches!(collection.mode, CollectionMode::Fungible(_)) {1833			collection.log(ERC20Events::Approval {1834                owner: *from.as_eth(),1835                spender: *sender.as_eth(),1836                value: allowance.into()1837            });1838		}18391840		Ok(())1841	}18421843    pub fn set_variable_meta_data_internal(1844        sender: &T::CrossAccountId,1845        collection: &CollectionHandle<T>, 1846        item_id: TokenId,1847        data: Vec<u8>,1848    ) -> DispatchResult {1849        Self::token_exists(&collection, item_id)?;18501851        ensure!(ChainLimit::get().custom_data_limit >= data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);18521853        // Modify permissions check1854        ensure!(Self::is_item_owner(&sender, &collection, item_id) ||1855            Self::is_owner_or_admin_permissions(&collection, &sender),1856            Error::<T>::NoPermission);18571858        match collection.mode1859        {1860            CollectionMode::NFT => Self::set_nft_variable_data(&collection, item_id, data)?,1861            CollectionMode::ReFungible  => Self::set_re_fungible_variable_data(&collection, item_id, data)?,1862            CollectionMode::Fungible(_) => fail!(Error::<T>::CantStoreMetadataInFungibleTokens),1863            _ => fail!(Error::<T>::UnexpectedCollectionType)1864        };18651866        Ok(())1867    }18681869    pub fn create_multiple_items_internal(1870        sender: &T::CrossAccountId,1871        collection: &CollectionHandle<T>,1872        owner: &T::CrossAccountId,1873        items_data: Vec<CreateItemData>,1874    ) -> DispatchResult {1875        Self::can_create_items_in_collection(&collection, &sender, &owner, items_data.len() as u32)?;18761877        for data in &items_data {1878            Self::validate_create_item_args(&collection, data)?;1879        }1880        for data in &items_data {1881            Self::create_item_no_validation(&collection, owner, data.clone())?;1882        }18831884        Ok(())1885    }18861887    pub fn burn_item_internal(1888        sender: &T::CrossAccountId,1889        collection: &CollectionHandle<T>,1890        item_id: TokenId,1891        value: u128,1892    ) -> DispatchResult {1893        ensure!(1894            Self::is_item_owner(&sender, &collection, item_id) ||1895            (1896                collection.limits.owner_can_transfer &&1897                Self::is_owner_or_admin_permissions(&collection, &sender)1898            ),1899            Error::<T>::NoPermission1900        );19011902        if collection.access == AccessMode::WhiteList {1903            Self::check_white_list(&collection, &sender)?;1904        }19051906        match collection.mode1907        {1908            CollectionMode::NFT => Self::burn_nft_item(&collection, item_id)?,1909            CollectionMode::Fungible(_)  => Self::burn_fungible_item(&sender, &collection, value)?,1910            CollectionMode::ReFungible  => Self::burn_refungible_item(&collection, item_id, &sender)?,1911            _ => ()1912        };19131914        Ok(())1915    }19161917    pub fn toggle_white_list_internal(1918        sender: &T::CrossAccountId,1919        collection: &CollectionHandle<T>,1920        address: &T::CrossAccountId,1921        whitelisted: bool,1922    ) -> DispatchResult {1923        Self::check_owner_or_admin_permissions(&collection, &sender)?;19241925        if whitelisted {1926            <WhiteList<T>>::insert(collection.id, address.as_sub(), true);1927        } else {1928            <WhiteList<T>>::remove(collection.id, address.as_sub());1929        }19301931        Ok(())1932    }19331934    fn is_correct_transfer(collection: &CollectionHandle<T>, recipient: &T::CrossAccountId) -> DispatchResult {1935        let collection_id = collection.id;19361937        // check token limit and account token limit1938        let account_items: u32 = <AddressTokens<T>>::get(collection_id, recipient.as_sub()).len() as u32;1939        ensure!(collection.limits.account_token_ownership_limit > account_items,  Error::<T>::AccountTokenLimitExceeded);1940        1941        Ok(())1942    }19431944    fn can_create_items_in_collection(collection: &CollectionHandle<T>, sender: &T::CrossAccountId, owner: &T::CrossAccountId, amount: u32) -> DispatchResult {1945        let collection_id = collection.id;19461947        // check token limit and account token limit1948        let total_items: u32 = ItemListIndex::get(collection_id)1949            .checked_add(amount)1950            .ok_or(Error::<T>::CollectionTokenLimitExceeded)?;1951        let account_items: u32 = (<AddressTokens<T>>::get(collection_id, owner.as_sub()).len() as u32)1952            .checked_add(amount)1953            .ok_or(Error::<T>::AccountTokenLimitExceeded)?;1954        ensure!(collection.limits.token_limit >= total_items,  Error::<T>::CollectionTokenLimitExceeded);1955        ensure!(collection.limits.account_token_ownership_limit >= account_items,  Error::<T>::AccountTokenLimitExceeded);19561957        if !Self::is_owner_or_admin_permissions(collection, &sender) {1958            ensure!(collection.mint_mode == true, Error::<T>::PublicMintingNotAllowed);1959            Self::check_white_list(collection, owner)?;1960            Self::check_white_list(collection, sender)?;1961        }19621963        Ok(())1964    }19651966    fn validate_create_item_args(target_collection: &CollectionHandle<T>, data: &CreateItemData) -> DispatchResult {1967        match target_collection.mode1968        {1969            CollectionMode::NFT => {1970                if let CreateItemData::NFT(data) = data {1971                    // check sizes1972                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1973                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);1974                } else {1975                    fail!(Error::<T>::NotNftDataUsedToMintNftCollectionToken);1976                }1977            },1978            CollectionMode::Fungible(_) => {1979                if let CreateItemData::Fungible(_) = data {1980                } else {1981                    fail!(Error::<T>::NotFungibleDataUsedToMintFungibleCollectionToken);1982                }1983            },1984            CollectionMode::ReFungible => {1985                if let CreateItemData::ReFungible(data) = data {19861987                    // check sizes1988                    ensure!(ChainLimit::get().custom_data_limit >= data.const_data.len() as u32, Error::<T>::TokenConstDataLimitExceeded);1989                    ensure!(ChainLimit::get().custom_data_limit >= data.variable_data.len() as u32, Error::<T>::TokenVariableDataLimitExceeded);19901991                    // Check refungibility limits1992                    ensure!(data.pieces <= MAX_REFUNGIBLE_PIECES, Error::<T>::WrongRefungiblePieces);1993                    ensure!(data.pieces > 0, Error::<T>::WrongRefungiblePieces);1994                } else {1995                    fail!(Error::<T>::NotReFungibleDataUsedToMintReFungibleCollectionToken);1996                }1997            },1998            _ => { fail!(Error::<T>::UnexpectedCollectionType); }1999        };20002001        Ok(())2002    }20032004    fn create_item_no_validation(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, data: CreateItemData) -> DispatchResult {2005        match data2006        {2007            CreateItemData::NFT(data) => {2008                let item = NftItemType {2009                    owner: owner.clone(),2010                    const_data: data.const_data,2011                    variable_data: data.variable_data2012                };20132014                Self::add_nft_item(collection, item)?;2015            },2016            CreateItemData::Fungible(data) => {2017                Self::add_fungible_item(collection, &owner, data.value)?;2018            },2019            CreateItemData::ReFungible(data) => {2020                let mut owner_list = Vec::new();2021                owner_list.push(Ownership {owner: owner.clone(), fraction: data.pieces});20222023                let item = ReFungibleItemType {2024                    owner: owner_list,2025                    const_data: data.const_data,2026                    variable_data: data.variable_data2027                };20282029                Self::add_refungible_item(collection, item)?;2030            }2031        };20322033        Ok(())2034    }20352036    fn add_fungible_item(collection: &CollectionHandle<T>, owner: &T::CrossAccountId, value: u128) -> DispatchResult {2037        let collection_id = collection.id;20382039        // Does new owner already have an account?2040        let balance: u128 = <FungibleItemList<T>>::get(collection_id, owner.as_sub()).value;20412042        // Mint 2043        let item = FungibleItemType {2044            value: balance.checked_add(value).ok_or(Error::<T>::NumOverflow)?,2045        };2046        <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), item);20472048        // Update balance2049        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2050            .checked_add(value)2051            .ok_or(Error::<T>::NumOverflow)?;2052        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20532054        Self::deposit_event(RawEvent::ItemCreated(collection_id, 0, owner.clone()));2055        Ok(())2056    }20572058    fn add_refungible_item(collection: &CollectionHandle<T>, item: ReFungibleItemType<T::CrossAccountId>) -> DispatchResult {2059        let collection_id = collection.id;20602061        let current_index = <ItemListIndex>::get(collection_id)2062            .checked_add(1)2063            .ok_or(Error::<T>::NumOverflow)?;2064        let itemcopy = item.clone();20652066        ensure!(2067            item.owner.len() == 1,2068            Error::<T>::BadCreateRefungibleCall,2069        );2070        let item_owner = item.owner.first().expect("only one owner is defined");20712072        let value = item_owner.fraction;2073        let owner = item_owner.owner.clone();20742075        Self::add_token_index(collection_id, current_index, &owner)?;20762077        <ItemListIndex>::insert(collection_id, current_index);2078        <ReFungibleItemList<T>>::insert(collection_id, current_index, itemcopy);20792080        // Update balance2081        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2082            .checked_add(value)2083            .ok_or(Error::<T>::NumOverflow)?;2084        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);20852086        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, owner));2087        Ok(())2088    }20892090    fn add_nft_item(collection: &CollectionHandle<T>, item: NftItemType<T::CrossAccountId>) -> DispatchResult {2091        let collection_id = collection.id;20922093        let current_index = <ItemListIndex>::get(collection_id)2094            .checked_add(1)2095            .ok_or(Error::<T>::NumOverflow)?;20962097        let item_owner = item.owner.clone();2098        Self::add_token_index(collection_id, current_index, &item.owner)?;20992100        <ItemListIndex>::insert(collection_id, current_index);2101        <NftItemList<T>>::insert(collection_id, current_index, item);21022103        // Update balance2104        let new_balance = <Balance<T>>::get(collection_id, item_owner.as_sub())2105            .checked_add(1)2106            .ok_or(Error::<T>::NumOverflow)?;2107        <Balance<T>>::insert(collection_id, item_owner.as_sub(), new_balance);21082109        collection.log(ERC721Events::Transfer {2110            from: H160::default(),2111            to: *item_owner.as_eth(),2112            token_id: current_index.into(),2113        });2114        Self::deposit_event(RawEvent::ItemCreated(collection_id, current_index, item_owner));2115        Ok(())2116    }21172118    fn burn_refungible_item(2119        collection: &CollectionHandle<T>,2120        item_id: TokenId,2121        owner: &T::CrossAccountId,2122    ) -> DispatchResult {2123        let collection_id = collection.id;21242125        let mut token = <ReFungibleItemList<T>>::get(collection_id, item_id)2126            .ok_or(Error::<T>::TokenNotFound)?;2127        let rft_balance = token2128            .owner2129            .iter()2130            .find(|&i| i.owner == *owner)2131            .ok_or(Error::<T>::TokenNotFound)?;2132        Self::remove_token_index(collection_id, item_id, owner)?;21332134        // update balance2135        let new_balance = <Balance<T>>::get(collection_id, rft_balance.owner.as_sub())2136            .checked_sub(rft_balance.fraction)2137            .ok_or(Error::<T>::NumOverflow)?;2138        <Balance<T>>::insert(collection_id, rft_balance.owner.as_sub(), new_balance);21392140        // Re-create owners list with sender removed2141        let index = token2142            .owner2143            .iter()2144            .position(|i| i.owner == *owner)2145            .expect("owned item is exists");2146        token.owner.remove(index);2147        let owner_count = token.owner.len();21482149        // Burn the token completely if this was the last (only) owner2150        if owner_count == 0 {2151            <ReFungibleItemList<T>>::remove(collection_id, item_id);2152            <VariableMetaDataBasket<T>>::remove(collection_id, item_id);2153        }2154        else {2155            <ReFungibleItemList<T>>::insert(collection_id, item_id, token);2156        }21572158        Ok(())2159    }21602161    fn burn_nft_item(collection: &CollectionHandle<T>, item_id: TokenId) -> DispatchResult {2162        let collection_id = collection.id;21632164        let item = <NftItemList<T>>::get(collection_id, item_id)2165            .ok_or(Error::<T>::TokenNotFound)?;2166        Self::remove_token_index(collection_id, item_id, &item.owner)?;21672168        // update balance2169        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2170            .checked_sub(1)2171            .ok_or(Error::<T>::NumOverflow)?;2172        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2173        <NftItemList<T>>::remove(collection_id, item_id);2174        <VariableMetaDataBasket<T>>::remove(collection_id, item_id);21752176        Self::deposit_event(RawEvent::ItemDestroyed(collection.id, item_id));2177        Ok(())2178    }21792180    fn burn_fungible_item(owner: &T::CrossAccountId, collection: &CollectionHandle<T>, value: u128) -> DispatchResult {2181        let collection_id = collection.id;21822183        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2184        ensure!(balance.value >= value, Error::<T>::TokenValueNotEnough);21852186        // update balance2187        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2188            .checked_sub(value)2189            .ok_or(Error::<T>::NumOverflow)?;2190        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);21912192        if balance.value - value > 0 {2193            balance.value -= value;2194            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2195        }2196        else {2197            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2198        }21992200        collection.log(ERC20Events::Transfer {2201            from: *owner.as_eth(),2202            to: H160::default(),2203            value: value.into(),2204        });2205        Ok(())2206    }22072208    pub fn get_collection(collection_id: CollectionId) -> Result<CollectionHandle<T>, sp_runtime::DispatchError> {2209        Ok(<CollectionHandle<T>>::get(collection_id)2210            .ok_or(Error::<T>::CollectionNotFound)?)2211    }22122213    fn save_collection(collection: CollectionHandle<T>) {2214        <CollectionById<T>>::insert(collection.id, collection.into_inner());2215    }22162217    pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {2218        if collection.logs.is_empty() {2219            return Ok(())2220        }2221        T::EthereumTransactionSender::submit_logs_transaction(2222            eth::generate_transaction(collection.id, T::EthereumChainId::get()),2223            collection.logs.retrieve_logs(),2224        )2225    }22262227    fn check_owner_permissions(target_collection: &CollectionHandle<T>, subject: &T::AccountId) -> DispatchResult {2228        ensure!(2229            *subject == target_collection.owner,2230            Error::<T>::NoPermission2231        );22322233        Ok(())2234    }22352236    fn is_owner_or_admin_permissions(collection: &CollectionHandle<T>, subject: &T::CrossAccountId) -> bool {2237        *subject.as_sub() == collection.owner || <AdminList<T>>::get(collection.id).contains(&subject)2238    }22392240    fn check_owner_or_admin_permissions(2241        collection: &CollectionHandle<T>,2242        subject: &T::CrossAccountId,2243    ) -> DispatchResult {2244        ensure!(Self::is_owner_or_admin_permissions(collection, subject), Error::<T>::NoPermission);22452246        Ok(())2247    }22482249    fn owned_amount(2250        subject: &T::CrossAccountId,2251        target_collection: &CollectionHandle<T>,2252        item_id: TokenId,2253    ) -> Option<u128> {2254        let collection_id = target_collection.id;22552256        match target_collection.mode {2257            CollectionMode::NFT => (<NftItemList<T>>::get(collection_id, item_id)?.owner == *subject)2258                .then(|| 1),2259            CollectionMode::Fungible(_) => Some(<FungibleItemList<T>>::get(collection_id, &subject.as_sub())2260                .value),2261            CollectionMode::ReFungible => <ReFungibleItemList<T>>::get(collection_id, item_id)?2262                .owner2263                .iter()2264                .find(|i| i.owner == *subject)2265                .map(|i| i.fraction),2266            CollectionMode::Invalid => None,2267        }2268    }22692270    fn is_item_owner(subject: &T::CrossAccountId, target_collection: &CollectionHandle<T>, item_id: TokenId) -> bool {2271        match target_collection.mode {2272            CollectionMode::Fungible(_) => true,2273            _ => Self::owned_amount(&subject, target_collection, item_id).is_some(),2274        }2275    }22762277    fn check_white_list(collection: &CollectionHandle<T>, address: &T::CrossAccountId) -> DispatchResult {2278        let collection_id = collection.id;22792280        let mes = Error::<T>::AddresNotInWhiteList;2281        ensure!(<WhiteList<T>>::contains_key(collection_id, address.as_sub()), mes);22822283        Ok(())2284    }22852286    /// Check if token exists. In case of Fungible, check if there is an entry for 2287    /// the owner in fungible balances double map2288    fn token_exists(2289        target_collection: &CollectionHandle<T>,2290        item_id: TokenId,2291    ) -> DispatchResult {2292        let collection_id = target_collection.id;2293        let exists = match target_collection.mode2294        {2295            CollectionMode::NFT => <NftItemList<T>>::contains_key(collection_id, item_id),2296            CollectionMode::Fungible(_)  => true,2297            CollectionMode::ReFungible  => <ReFungibleItemList<T>>::contains_key(collection_id, item_id),2298            _ => false2299        };23002301        ensure!(exists == true, Error::<T>::TokenNotFound);2302        Ok(())2303    }23042305    fn transfer_fungible(2306        collection: &CollectionHandle<T>,2307        value: u128,2308        owner: &T::CrossAccountId,2309        recipient: &T::CrossAccountId,2310    ) -> DispatchResult {2311        let collection_id = collection.id;23122313        let mut balance = <FungibleItemList<T>>::get(collection_id, owner.as_sub());2314        ensure!(balance.value >= value, Error::<T>::TokenValueTooLow);23152316        // Send balance to recipient (updates balanceOf of recipient)2317        Self::add_fungible_item(collection, recipient, value)?;23182319        // update balanceOf of sender2320        <Balance<T>>::insert(collection_id, owner.as_sub(), balance.value - value);23212322        // Reduce or remove sender2323        if balance.value == value {2324            <FungibleItemList<T>>::remove(collection_id, owner.as_sub());2325        }2326        else {2327            balance.value -= value;2328            <FungibleItemList<T>>::insert(collection_id, owner.as_sub(), balance);2329        }23302331        collection.log(ERC20Events::Transfer {2332            from: *owner.as_eth(),2333            to: *recipient.as_eth(),2334            value: value.into(),2335        });2336        Self::deposit_event(RawEvent::Transfer(collection.id, 1, owner.clone(), recipient.clone(), value));23372338        Ok(())2339    }23402341    fn transfer_refungible(2342        collection: &CollectionHandle<T>,2343        item_id: TokenId,2344        value: u128,2345        owner: T::CrossAccountId,2346        new_owner: T::CrossAccountId,2347    ) -> DispatchResult {2348        let collection_id = collection.id;2349        let full_item = <ReFungibleItemList<T>>::get(collection_id, item_id)2350            .ok_or(Error::<T>::TokenNotFound)?;23512352        let item = full_item2353            .owner2354            .iter()2355            .filter(|i| i.owner == owner)2356            .next()2357            .ok_or(Error::<T>::TokenNotFound)?;2358        let amount = item.fraction;23592360        ensure!(amount >= value, Error::<T>::TokenValueTooLow);23612362        // update balance2363        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2364            .checked_sub(value)2365            .ok_or(Error::<T>::NumOverflow)?;2366        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);23672368        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2369            .checked_add(value)2370            .ok_or(Error::<T>::NumOverflow)?;2371        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);23722373        let old_owner = item.owner.clone();2374        let new_owner_has_account = full_item.owner.iter().any(|i| i.owner == new_owner);23752376        // transfer2377        if amount == value && !new_owner_has_account {2378            // change owner2379            // new owner do not have account2380            let mut new_full_item = full_item.clone();2381            new_full_item2382                .owner2383                .iter_mut()2384                .find(|i| i.owner == owner)2385                .expect("old owner does present in refungible")2386                .owner = new_owner.clone();2387            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);23882389            // update index collection2390            Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;2391        } else {2392            let mut new_full_item = full_item.clone();2393            new_full_item2394                .owner2395                .iter_mut()2396                .find(|i| i.owner == owner)2397                .expect("old owner does present in refungible")2398                .fraction -= value;23992400            // separate amount2401            if new_owner_has_account {2402                // new owner has account2403                new_full_item2404                    .owner2405                    .iter_mut()2406                    .find(|i| i.owner == new_owner)2407                    .expect("new owner has account")2408                    .fraction += value;2409            } else {2410                // new owner do not have account2411                new_full_item.owner.push(Ownership {2412                    owner: new_owner.clone(),2413                    fraction: value,2414                });2415                Self::add_token_index(collection_id, item_id, &new_owner)?;2416            }24172418            <ReFungibleItemList<T>>::insert(collection_id, item_id, new_full_item);2419        }24202421        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, owner, new_owner, amount));24222423        Ok(())2424    }24252426    fn transfer_nft(2427        collection: &CollectionHandle<T>,2428        item_id: TokenId,2429        sender: T::CrossAccountId,2430        new_owner: T::CrossAccountId,2431    ) -> DispatchResult {2432        let collection_id = collection.id;2433        let mut item = <NftItemList<T>>::get(collection_id, item_id)2434            .ok_or(Error::<T>::TokenNotFound)?;24352436        ensure!(2437            sender == item.owner,2438            Error::<T>::MustBeTokenOwner2439        );24402441        // update balance2442        let balance_old_owner = <Balance<T>>::get(collection_id, item.owner.as_sub())2443            .checked_sub(1)2444            .ok_or(Error::<T>::NumOverflow)?;2445        <Balance<T>>::insert(collection_id, item.owner.as_sub(), balance_old_owner);24462447        let balance_new_owner = <Balance<T>>::get(collection_id, new_owner.as_sub())2448            .checked_add(1)2449            .ok_or(Error::<T>::NumOverflow)?;2450        <Balance<T>>::insert(collection_id, new_owner.as_sub(), balance_new_owner);24512452        // change owner2453        let old_owner = item.owner.clone();2454        item.owner = new_owner.clone();2455        <NftItemList<T>>::insert(collection_id, item_id, item);24562457        // update index collection2458        Self::move_token_index(collection_id, item_id, &old_owner, &new_owner)?;24592460        collection.log(ERC721Events::Transfer {2461            from: *sender.as_eth(),2462            to: *new_owner.as_eth(),2463            token_id: item_id.into(),2464        });2465        Self::deposit_event(RawEvent::Transfer(collection.id, item_id, sender, new_owner, 1));24662467        Ok(())2468    }2469    2470    fn set_re_fungible_variable_data(2471        collection: &CollectionHandle<T>,2472        item_id: TokenId,2473        data: Vec<u8>2474    ) -> DispatchResult {2475        let collection_id = collection.id;2476        let mut item = <ReFungibleItemList<T>>::get(collection_id, item_id)2477            .ok_or(Error::<T>::TokenNotFound)?;24782479        item.variable_data = data;24802481        <ReFungibleItemList<T>>::insert(collection_id, item_id, item);24822483        Ok(())2484    }24852486    fn set_nft_variable_data(2487        collection: &CollectionHandle<T>,2488        item_id: TokenId,2489        data: Vec<u8>2490    ) -> DispatchResult {2491        let collection_id = collection.id;2492        let mut item = <NftItemList<T>>::get(collection_id, item_id)2493            .ok_or(Error::<T>::TokenNotFound)?;2494        2495        item.variable_data = data;24962497        <NftItemList<T>>::insert(collection_id, item_id, item);2498        2499        Ok(())2500    }25012502    fn init_collection(item: &Collection<T>) {2503        // check params2504        assert!(2505            item.decimal_points <= MAX_DECIMAL_POINTS,2506            "decimal_points parameter must be lower than MAX_DECIMAL_POINTS"2507        );2508        assert!(2509            item.name.len() <= 64,2510            "Collection name can not be longer than 63 char"2511        );2512        assert!(2513            item.name.len() <= 256,2514            "Collection description can not be longer than 255 char"2515        );2516        assert!(2517            item.token_prefix.len() <= 16,2518            "Token prefix can not be longer than 15 char"2519        );25202521        // Generate next collection ID2522        let next_id = CreatedCollectionCount::get()2523            .checked_add(1)2524            .unwrap();25252526        CreatedCollectionCount::put(next_id);2527    }25282529    fn init_nft_token(collection_id: CollectionId, item: &NftItemType<T::CrossAccountId>) {2530        let current_index = <ItemListIndex>::get(collection_id)2531            .checked_add(1)2532            .unwrap();25332534        Self::add_token_index(collection_id, current_index, &item.owner).unwrap();25352536        <ItemListIndex>::insert(collection_id, current_index);25372538        // Update balance2539        let new_balance = <Balance<T>>::get(collection_id, item.owner.as_sub())2540            .checked_add(1)2541            .unwrap();2542        <Balance<T>>::insert(collection_id, item.owner.as_sub(), new_balance);2543    }25442545    fn init_fungible_token(collection_id: CollectionId, owner: &T::CrossAccountId, item: &FungibleItemType) {2546        let current_index = <ItemListIndex>::get(collection_id)2547            .checked_add(1)2548            .unwrap();25492550        Self::add_token_index(collection_id, current_index, owner).unwrap();25512552        <ItemListIndex>::insert(collection_id, current_index);25532554        // Update balance2555        let new_balance = <Balance<T>>::get(collection_id, owner.as_sub())2556            .checked_add(item.value)2557            .unwrap();2558        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2559    }25602561    fn init_refungible_token(collection_id: CollectionId, item: &ReFungibleItemType<T::CrossAccountId>) {2562        let current_index = <ItemListIndex>::get(collection_id)2563            .checked_add(1)2564            .unwrap();25652566        let value = item.owner.first().unwrap().fraction;2567        let owner = item.owner.first().unwrap().owner.clone();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(value)2576            .unwrap();2577        <Balance<T>>::insert(collection_id, owner.as_sub(), new_balance);2578    }25792580    fn add_token_index(collection_id: CollectionId, item_index: TokenId, owner: &T::CrossAccountId) -> DispatchResult {2581        // add to account limit2582        if <AccountItemCount<T>>::contains_key(owner.as_sub()) {25832584            // bound Owned tokens by a single address2585            let count = <AccountItemCount<T>>::get(owner.as_sub());2586            ensure!(count < ChainLimit::get().account_token_ownership_limit, Error::<T>::AddressOwnershipLimitExceeded);25872588            <AccountItemCount<T>>::insert(owner.as_sub(), count2589                .checked_add(1)2590                .ok_or(Error::<T>::NumOverflow)?);2591        }2592        else {2593            <AccountItemCount<T>>::insert(owner.as_sub(), 1);2594        }25952596        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2597        if list_exists {2598            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2599            let item_contains = list.contains(&item_index.clone());26002601            if !item_contains {2602                list.push(item_index.clone());2603            }26042605            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2606        } else {2607            let mut itm = Vec::new();2608            itm.push(item_index.clone());2609            <AddressTokens<T>>::insert(collection_id, owner.as_sub(), itm);2610        }26112612        Ok(())2613    }26142615    fn remove_token_index(2616        collection_id: CollectionId,2617        item_index: TokenId,2618        owner: &T::CrossAccountId,2619    ) -> DispatchResult {26202621        // update counter2622        <AccountItemCount<T>>::insert(owner.as_sub(), 2623            <AccountItemCount<T>>::get(owner.as_sub())2624            .checked_sub(1)2625            .ok_or(Error::<T>::NumOverflow)?);262626272628        let list_exists = <AddressTokens<T>>::contains_key(collection_id, owner.as_sub());2629        if list_exists {2630            let mut list = <AddressTokens<T>>::get(collection_id, owner.as_sub());2631            let item_contains = list.contains(&item_index.clone());26322633            if item_contains {2634                list.retain(|&item| item != item_index);2635                <AddressTokens<T>>::insert(collection_id, owner.as_sub(), list);2636            }2637        }26382639        Ok(())2640    }26412642    fn move_token_index(2643        collection_id: CollectionId,2644        item_index: TokenId,2645        old_owner: &T::CrossAccountId,2646        new_owner: &T::CrossAccountId,2647    ) -> DispatchResult {2648        Self::remove_token_index(collection_id, item_index, old_owner)?;2649        Self::add_token_index(collection_id, item_index, new_owner)?;26502651        Ok(())2652    }2653    2654    fn ensure_contract_owned(account: T::AccountId, contract: &T::AccountId) -> DispatchResult {2655        ensure!(<ContractOwner<T>>::get(contract) == Some(account), Error::<T>::NoPermission);26562657        Ok(())2658    }2659}26602661////////////////////////////////////////////////////////////////////////////////////////////////////2662// Economic models2663// #region26642665/// Fee multiplier.2666pub type Multiplier = FixedU128;26672668type BalanceOf<T> = <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::Balance;26692670/// Require the transactor pay for themselves and maybe include a tip to gain additional priority2671/// in the queue.2672#[derive(Encode, Decode, Clone, Eq, PartialEq)]2673pub struct ChargeTransactionPayment<T: Config>(#[codec(compact)] BalanceOf<T>);26742675impl<T: Config + Send + Sync> sp_std::fmt::Debug 2676    for ChargeTransactionPayment<T>2677{2678	#[cfg(feature = "std")]2679	fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2680		write!(f, "ChargeTransactionPayment<{:?}>", self.0)2681	}2682	#[cfg(not(feature = "std"))]2683	fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {2684		Ok(())2685	}2686}26872688impl<T: Config> ChargeTransactionPayment<T>2689where2690    T::Call: Dispatchable<Info=DispatchInfo, PostInfo=PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2691    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2692    T::AccountId: AsRef<[u8]>,2693    T::AccountId: UncheckedFrom<T::Hash>,2694{2695    fn traditional_fee(2696        len: usize,2697        info: &DispatchInfoOf<T::Call>,2698        tip: BalanceOf<T>,2699    ) -> BalanceOf<T>2700    where2701        T::Call: Dispatchable<Info = DispatchInfo>,2702    {2703        <pallet_transaction_payment::Module<T>>::compute_fee(len as u32, info, tip)2704    }27052706	fn get_priority(len: usize, info: &DispatchInfoOf<T::Call>, final_fee: BalanceOf<T>) -> TransactionPriority {2707        let weight_saturation = T::BlockWeights::get().max_block / info.weight.max(1);2708        let max_block_length = *T::BlockLength::get().max.get(DispatchClass::Normal);2709        let len_saturation = max_block_length as u64 / (len as u64).max(1);2710        let coefficient: BalanceOf<T> = weight_saturation2711            .min(len_saturation)2712            .saturated_into::<BalanceOf<T>>();2713        final_fee2714            .saturating_mul(coefficient)2715            .saturated_into::<TransactionPriority>()2716    }27172718    fn withdraw_fee(2719        &self,2720        who: &T::AccountId,2721        call: &T::Call,2722        info: &DispatchInfoOf<T::Call>,2723        len: usize,2724	) -> Result<2725		(2726			BalanceOf<T>,2727			<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,2728		),2729		TransactionValidityError,2730	> {2731        let tip = self.0;27322733        let fee = Self::traditional_fee(len, info, tip);27342735        // Only mess with balances if fee is not zero.2736        if fee.is_zero() {2737            return <<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(who, call, info, fee, tip)2738			.map(|i| (fee, i));2739        }27402741        // Determine who is paying transaction fee based on ecnomic model2742        // Parse call to extract collection ID and access collection sponsor2743        let mut sponsor: Option<T::AccountId> = (|| match IsSubType::<Call<T>>::is_sub_type(call) {2744            Some(Call::create_item(collection_id, _owner, _properties)) => {2745                let collection = <CollectionById<T>>::get(collection_id)?;27462747                // sponsor timeout2748                let block_number = <system::Module<T>>::block_number() as T::BlockNumber;27492750                let limit = collection.limits.sponsor_transfer_timeout;2751                if <CreateItemBasket<T>>::contains_key((collection_id, &who)) {2752                    let last_tx_block = <CreateItemBasket<T>>::get((collection_id, &who));2753                    let limit_time = last_tx_block + limit.into();2754                    if block_number <= limit_time {2755                        return None;2756                    }2757                }2758                <CreateItemBasket<T>>::insert((collection_id, who.clone()), block_number);27592760                // check free create limit2761                if collection.limits.sponsored_data_size >= (_properties.len() as u32) {2762                    collection.sponsorship.sponsor()2763                        .cloned()2764                } else {2765                    None2766                }2767            }2768            Some(Call::transfer(_new_owner, collection_id, item_id, _value)) => {2769                let collection = <CollectionById<T>>::get(collection_id)?;2770                2771                let mut sponsor_transfer = false;2772                if collection.sponsorship.confirmed() {27732774                    let collection_limits = collection.limits;2775                    let collection_mode = collection.mode;2776    2777                    // sponsor timeout2778                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2779                    sponsor_transfer = match collection_mode {2780                        CollectionMode::NFT => {2781    2782                            // get correct limit2783                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2784                                collection_limits.sponsor_transfer_timeout2785                            } else {2786                                ChainLimit::get().nft_sponsor_transfer_timeout2787                            };2788    2789                            let mut sponsored = true;2790                            if <NftTransferBasket<T>>::contains_key(collection_id, item_id) {2791                                let last_tx_block = <NftTransferBasket<T>>::get(collection_id, item_id);2792                                let limit_time = last_tx_block + limit.into();2793                                if block_number <= limit_time {2794                                    sponsored = false;2795                                }2796                            }2797                            if sponsored {2798                                <NftTransferBasket<T>>::insert(collection_id, item_id, block_number);2799                            }28002801                            sponsored2802                        }2803                        CollectionMode::Fungible(_) => {2804    2805                            // get correct limit2806                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2807                                collection_limits.sponsor_transfer_timeout2808                            } else {2809                                ChainLimit::get().fungible_sponsor_transfer_timeout2810                            };2811    2812                            let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2813                            let mut sponsored = true;2814                            if <FungibleTransferBasket<T>>::contains_key(collection_id, who) {2815                                let last_tx_block = <FungibleTransferBasket<T>>::get(collection_id, who);2816                                let limit_time = last_tx_block + limit.into();2817                                if block_number <= limit_time {2818                                    sponsored = false;2819                                }2820                            }2821                            if sponsored {2822                                <FungibleTransferBasket<T>>::insert(collection_id, who, block_number);2823                            }28242825                            sponsored2826                        }2827                        CollectionMode::ReFungible => {2828    2829                            // get correct limit2830                            let limit: u32 = if collection_limits.sponsor_transfer_timeout > 0 {2831                                collection_limits.sponsor_transfer_timeout2832                            } else {2833                                ChainLimit::get().refungible_sponsor_transfer_timeout2834                            };2835    2836                            let mut sponsored = true;2837                            if <ReFungibleTransferBasket<T>>::contains_key(collection_id, item_id) {2838                                let last_tx_block = <ReFungibleTransferBasket<T>>::get(collection_id, item_id);2839                                let limit_time = last_tx_block + limit.into();2840                                if block_number <= limit_time {2841                                    sponsored = false;2842                                }2843                            }2844                            if sponsored {2845                                <ReFungibleTransferBasket<T>>::insert(collection_id, item_id, block_number);2846                            }28472848                            sponsored2849                        }2850                        _ => {2851                            false2852                        },2853                    };2854                }28552856                if !sponsor_transfer {2857                    None2858                } else {2859                    collection.sponsorship.sponsor()2860                        .cloned()2861                }2862            }28632864            Some(Call::set_variable_meta_data(collection_id, item_id, data)) => {2865                let mut sponsor_metadata_changes = false;28662867                let collection = <CollectionById<T>>::get(collection_id)?;28682869                if2870                    collection.sponsorship.confirmed() &&2871                    // Can't sponsor fungible collection, this tx will be rejected2872                    // as invalid2873                    !matches!(collection.mode, CollectionMode::Fungible(_)) &&2874                    data.len() <= collection.limits.sponsored_data_size as usize2875                {2876                    if let Some(rate_limit) = collection.limits.sponsored_data_rate_limit {2877                        let block_number = <system::Module<T>>::block_number() as T::BlockNumber;28782879                        if <VariableMetaDataBasket<T>>::get(collection_id, item_id)2880                            .map(|last_block| block_number - last_block > rate_limit)2881                            .unwrap_or(true) 2882                        {2883                            sponsor_metadata_changes = true;2884                            <VariableMetaDataBasket<T>>::insert(collection_id, item_id, block_number);2885                        }2886                    }2887                }28882889                if !sponsor_metadata_changes {2890                    None2891                } else {2892                    collection.sponsorship.sponsor().cloned()2893                }2894            }28952896            _ => None,2897        })();28982899        match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {2900            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29012902                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29032904                let owned_contract = <ContractOwner<T>>::get(called_contract.clone()).as_ref() == Some(who);2905                let white_list_enabled = <ContractWhiteListEnabled<T>>::contains_key(called_contract.clone());2906                  2907                if !owned_contract && white_list_enabled {2908                    if !<ContractWhiteList<T>>::contains_key(called_contract.clone(), who) {2909                        return Err(InvalidTransaction::Call.into());2910                    }2911                }2912            },2913            _ => {},2914        }29152916        // Sponsor smart contracts2917        sponsor = sponsor.or_else(|| match IsSubType::<pallet_contracts::Call<T>>::is_sub_type(call) {29182919            // On instantiation: set the contract owner2920            Some(pallet_contracts::Call::instantiate(_endowment, _gas_limit, code_hash, _data, salt)) => {29212922                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2923                    &who,2924                    code_hash,2925                    salt,2926                );2927                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29282929                None2930            },29312932            // On instantiation with code: set the contract owner2933            Some(pallet_contracts::Call::instantiate_with_code(_endowment, _gas_limit, _code, _data, _salt))  => {29342935                let new_contract_address = <pallet_contracts::Module<T>>::contract_address(2936                    &who,2937                    &T::Hashing::hash(&_code),2938                    _salt,2939                );29402941                <ContractOwner<T>>::insert(new_contract_address.clone(), who.clone());29422943                None2944            }29452946            // When the contract is called, check if the sponsoring is enabled and pay fees from contract endowment if it is2947            Some(pallet_contracts::Call::call(dest, _value, _gas_limit, _data)) => {29482949                let called_contract: T::AccountId = T::Lookup::lookup((*dest).clone()).unwrap_or(T::AccountId::default());29502951                let mut sponsor_transfer = false;2952                if <ContractSponsoringRateLimit<T>>::contains_key(called_contract.clone()) {2953                    let last_tx_block = <ContractSponsorBasket<T>>::get((&called_contract, &who));2954                    let block_number = <system::Module<T>>::block_number() as T::BlockNumber;2955                    let rate_limit = <ContractSponsoringRateLimit<T>>::get(&called_contract);2956                    let limit_time = last_tx_block + rate_limit;29572958                    if block_number >= limit_time {2959                        <ContractSponsorBasket<T>>::insert((called_contract.clone(), who.clone()), block_number);2960                        sponsor_transfer = true;2961                    }2962                } else {2963                    sponsor_transfer = false;2964                }2965               2966                if sponsor_transfer {2967                    if <ContractSelfSponsoring<T>>::contains_key(called_contract.clone()) {2968                        if <ContractSelfSponsoring<T>>::get(called_contract.clone()) {2969                            return Some(called_contract);2970                        }2971                    }2972                }29732974                None2975            },29762977            _ => None,2978        });29792980        let who_pays_fee = sponsor.unwrap_or_else(|| who.clone());29812982		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::withdraw_fee(&who_pays_fee, call, info, fee, tip)2983			.map(|i| (fee, i))2984    }2985}298629872988impl<T: Config + Send + Sync> SignedExtension2989    for ChargeTransactionPayment<T>2990where2991    BalanceOf<T>: Send + Sync + From<u64> + FixedPointOperand,2992    T::Call: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo> + IsSubType<Call<T>> + IsSubType<pallet_contracts::Call<T>>,2993    T::AccountId: AsRef<[u8]>,2994    T::AccountId: UncheckedFrom<T::Hash>,2995{2996    const IDENTIFIER: &'static str = "ChargeTransactionPayment";2997    type AccountId = T::AccountId;2998    type Call = T::Call;2999    type AdditionalSigned = ();3000    type Pre = (3001        // tip3002        BalanceOf<T>,3003        // who pays fee3004        Self::AccountId,3005		// imbalance resulting from withdrawing the fee3006		<<T as pallet_transaction_payment::Config>::OnChargeTransaction as pallet_transaction_payment::OnChargeTransaction<T>>::LiquidityInfo,3007    );3008    fn additional_signed(&self) -> sp_std::result::Result<(), TransactionValidityError> {3009        Ok(())3010    }30113012    fn validate(3013        &self,3014        who: &Self::AccountId,3015        call: &Self::Call,3016        info: &DispatchInfoOf<Self::Call>,3017        len: usize,3018    ) -> TransactionValidity {3019		let (fee, _) = self.withdraw_fee(who, call, info, len)?;3020		Ok(ValidTransaction {3021			priority: Self::get_priority(len, info, fee),3022			..Default::default()3023		})3024    }30253026    fn pre_dispatch(3027        self,3028        who: &Self::AccountId,3029        call: &Self::Call,3030        info: &DispatchInfoOf<Self::Call>,3031        len: usize,3032    ) -> Result<Self::Pre, TransactionValidityError> {3033        let (_fee, imbalance) = self.withdraw_fee(who, call, info, len)?;3034        Ok((self.0, who.clone(), imbalance))3035    }30363037    fn post_dispatch(3038        pre: Self::Pre,3039        info: &DispatchInfoOf<Self::Call>,3040        post_info: &PostDispatchInfoOf<Self::Call>,3041        len: usize,3042        _result: &DispatchResult,3043    ) -> Result<(), TransactionValidityError> {3044		let (tip, who, imbalance) = pre;3045		let actual_fee = pallet_transaction_payment::Module::<T>::compute_actual_fee(3046			len as u32,3047			info,3048			post_info,3049			tip,3050		);3051		<T as pallet_transaction_payment::Config>::OnChargeTransaction::correct_and_deposit_fee(&who, info, post_info, actual_fee, tip, imbalance)?;3052		Ok(())3053    }3054}30553056// #endregion30573058sp_api::decl_runtime_apis! {3059    pub trait NftApi {3060        /// Used for ethereum integration3061        fn eth_contract_code(account: H160) -> Option<Vec<u8>>;3062    }3063}